Merge "Pass full AttributionSourceState across Java/Cpp boundaries." into main
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb
index e898091..e8e7ec9 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "9653376"
+ build_id: "11947186"
target: "CtsShim"
source_file: "aosp_riscv64/CtsShimPriv.apk"
}
@@ -8,7 +8,7 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "master"
+ git_branch: "main"
transform: TRANSFORM_NONE
transform_options {
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb
index 04092366..6113b6a 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "9653376"
+ build_id: "11947186"
target: "CtsShim"
source_file: "aosp_riscv64/CtsShim.apk"
}
@@ -8,7 +8,7 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "master"
+ git_branch: "main"
transform: TRANSFORM_NONE
transform_options {
}
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 96d6f32..5e9af6a 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -405,6 +405,12 @@
aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
}
+cc_aconfig_library {
+ name: "android.companion.virtualdevice.flags-aconfig-cc-host",
+ aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
+ host_supported: true,
+}
+
java_aconfig_library {
name: "android.companion.virtualdevice.flags-aconfig-java",
aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index fb1fa3b..f0aa62c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -511,6 +511,9 @@
},
lint: {
baseline_filename: "lint-baseline.xml",
+ warning_checks: [
+ "FlaggedApi",
+ ],
},
jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
}
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 11da20a..87f1124 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -38,8 +38,8 @@
"--out-impl-jar $(location ravenwood.jar) " +
- "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
- "--gen-input-dump-file $(location hoststubgen_dump.txt) " +
+ "--gen-keep-all-file $(location hoststubgen_framework-minus-apex_keep_all.txt) " +
+ "--gen-input-dump-file $(location hoststubgen_framework-minus-apex_dump.txt) " +
"--in-jar $(location :framework-minus-apex-for-hoststubgen) " +
"--policy-override-file $(location :ravenwood-framework-policies) " +
@@ -54,14 +54,14 @@
"ravenwood.jar",
// Following files are created just as FYI.
- "hoststubgen_keep_all.txt",
- "hoststubgen_dump.txt",
+ "hoststubgen_framework-minus-apex_keep_all.txt",
+ "hoststubgen_framework-minus-apex_dump.txt",
"hoststubgen_framework-minus-apex.log",
"hoststubgen_framework-minus-apex_stats.csv",
"hoststubgen_framework-minus-apex_apis.csv",
],
- visibility: ["//visibility:private"],
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
}
// Extract the impl jar from "framework-minus-apex.ravenwood-base" for subsequent build rules.
@@ -79,43 +79,6 @@
],
}
-// Extract the stats file.
-genrule {
- name: "framework-minus-apex.ravenwood.stats",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":framework-minus-apex.ravenwood-base{hoststubgen_framework-minus-apex_stats.csv}",
- ],
- out: [
- "hoststubgen_framework-minus-apex_stats.csv",
- ],
-}
-
-genrule {
- name: "framework-minus-apex.ravenwood.apis",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":framework-minus-apex.ravenwood-base{hoststubgen_framework-minus-apex_apis.csv}",
- ],
- out: [
- "hoststubgen_framework-minus-apex_apis.csv",
- ],
-}
-
-genrule {
- name: "framework-minus-apex.ravenwood.keep_all",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":framework-minus-apex.ravenwood-base{hoststubgen_keep_all.txt}",
- ],
- out: [
- "hoststubgen_framework-minus-apex_keep_all.txt",
- ],
-}
-
java_library {
name: "services.core-for-hoststubgen",
installable: false, // host only jar.
@@ -138,8 +101,8 @@
"--out-impl-jar $(location ravenwood.jar) " +
- "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
- "--gen-input-dump-file $(location hoststubgen_dump.txt) " +
+ "--gen-keep-all-file $(location hoststubgen_services.core_keep_all.txt) " +
+ "--gen-input-dump-file $(location hoststubgen_services.core_dump.txt) " +
"--in-jar $(location :services.core-for-hoststubgen) " +
"--policy-override-file $(location :ravenwood-services-policies) " +
@@ -154,14 +117,14 @@
"ravenwood.jar",
// Following files are created just as FYI.
- "hoststubgen_keep_all.txt",
- "hoststubgen_dump.txt",
+ "hoststubgen_services.core_keep_all.txt",
+ "hoststubgen_services.core_dump.txt",
"hoststubgen_services.core.log",
"hoststubgen_services.core_stats.csv",
"hoststubgen_services.core_apis.csv",
],
- visibility: ["//visibility:private"],
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
}
java_genrule {
@@ -176,150 +139,42 @@
],
}
-// Extract the stats file.
-genrule {
- name: "services.core.ravenwood.stats",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":services.core.ravenwood-base{hoststubgen_services.core_stats.csv}",
- ],
- out: [
- "hoststubgen_services.core_stats.csv",
- ],
-}
-
-genrule {
- name: "services.core.ravenwood.apis",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":services.core.ravenwood-base{hoststubgen_services.core_apis.csv}",
- ],
- out: [
- "hoststubgen_services.core_apis.csv",
- ],
-}
-
-genrule {
- name: "services.core.ravenwood.keep_all",
- defaults: ["ravenwood-internal-only-visibility-genrule"],
- cmd: "cp $(in) $(out)",
- srcs: [
- ":services.core.ravenwood-base{hoststubgen_keep_all.txt}",
- ],
- out: [
- "hoststubgen_services.core_keep_all.txt",
- ],
-}
-
java_library {
name: "services.core.ravenwood-jarjar",
+ defaults: ["ravenwood-internal-only-visibility-java"],
installable: false,
static_libs: [
"services.core.ravenwood",
],
jarjar_rules: ":ravenwood-services-jarjar-rules",
- visibility: ["//visibility:private"],
-}
-
-java_library {
- name: "services.fakes.ravenwood-jarjar",
- installable: false,
- srcs: [":services.fakes-sources"],
- libs: [
- "ravenwood-framework",
- "services.core.ravenwood",
- ],
- jarjar_rules: ":ravenwood-services-jarjar-rules",
- visibility: ["//visibility:private"],
-}
-
-java_library {
- name: "mockito-ravenwood-prebuilt",
- installable: false,
- static_libs: [
- "mockito-robolectric-prebuilt",
- ],
-}
-
-java_library {
- name: "inline-mockito-ravenwood-prebuilt",
- installable: false,
- static_libs: [
- "inline-mockito-robolectric-prebuilt",
- ],
}
// Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
// Rename some of the dependencies to make sure they're included in the intended order.
-java_genrule {
+// Also apply jarjar.
+java_library {
name: "100-framework-minus-apex.ravenwood",
- cmd: "cp $(in) $(out)",
- srcs: [":framework-minus-apex.ravenwood"],
- out: ["100-framework-minus-apex.ravenwood.jar"],
- visibility: ["//visibility:private"],
+ defaults: ["ravenwood-internal-only-visibility-java"],
+ static_libs: [
+ "framework-minus-apex.ravenwood",
+ ],
+ sdk_version: "core_platform",
+ jarjar_rules: ":ravenwood-framework-jarjar-rules",
}
java_genrule {
// Use 200 to make sure it comes before the mainline stub ("all-updatable...").
name: "200-kxml2-android",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
cmd: "cp $(in) $(out)",
srcs: [":kxml2-android"],
out: ["200-kxml2-android.jar"],
- visibility: ["//visibility:private"],
}
java_genrule {
name: "z00-all-updatable-modules-system-stubs",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
cmd: "cp $(in) $(out)",
srcs: [":all-updatable-modules-system-stubs"],
out: ["z00-all-updatable-modules-system-stubs.jar"],
- visibility: ["//visibility:private"],
-}
-
-android_ravenwood_libgroup {
- name: "ravenwood-runtime",
- libs: [
- "100-framework-minus-apex.ravenwood",
- "200-kxml2-android",
-
- "ravenwood-runtime-common-ravenwood",
-
- "android.test.mock.ravenwood",
- "ravenwood-helper-runtime",
- "hoststubgen-helper-runtime.ravenwood",
- "services.core.ravenwood-jarjar",
- "services.fakes.ravenwood-jarjar",
-
- // Provide runtime versions of utils linked in below
- "junit",
- "truth",
- "flag-junit",
- "ravenwood-framework",
- "ravenwood-junit-impl",
- "ravenwood-junit-impl-flag",
- "mockito-ravenwood-prebuilt",
- "inline-mockito-ravenwood-prebuilt",
-
- // It's a stub, so it should be towards the end.
- "z00-all-updatable-modules-system-stubs",
- ],
- jni_libs: [
- "libandroid_runtime",
- "libravenwood_runtime",
- ],
-}
-
-android_ravenwood_libgroup {
- name: "ravenwood-utils",
- libs: [
- "junit",
- "truth",
- "flag-junit",
- "ravenwood-framework",
- "ravenwood-junit",
- "mockito-ravenwood-prebuilt",
- "inline-mockito-ravenwood-prebuilt",
- ],
}
diff --git a/SQLITE_OWNERS b/SQLITE_OWNERS
index 1ff72e7..783a0b6 100644
--- a/SQLITE_OWNERS
+++ b/SQLITE_OWNERS
@@ -1,2 +1,9 @@
+# Android platform SQLite owners are responsible for:
+# 1. Periodically updating libsqlite from upstream sqlite.org.
+# 2. Escalating libsqlite bug reports to upstream sqlite.org.
+# 3. Addressing bugs, performance regressions, and feature requests
+# in Android SDK SQLite wrappers (android.database.sqlite.*).
+# 4. Reviewing proposed changes to said Android SDK SQLite wrappers.
+
shayba@google.com
shombert@google.com
diff --git a/WEAR_OWNERS b/WEAR_OWNERS
index 4ffb239..52c07e0 100644
--- a/WEAR_OWNERS
+++ b/WEAR_OWNERS
@@ -12,3 +12,4 @@
shijianli@google.com
latkin@google.com
djsollen@google.com
+hdhabu@google.com
\ No newline at end of file
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index e092499..65bc8cc 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -44,6 +44,7 @@
"apct-perftests-resources-manager-apps",
"apct-perftests-utils",
"collector-device-lib",
+ "conscrypt-test-support",
"compatibility-device-util-axt",
"junit",
"junit-params",
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/BufferType.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/BufferType.java
new file mode 100644
index 0000000..bdc2a82
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/BufferType.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.conscrypt;
+
+import java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Enumeration that provides allocation of direct or heap buffers.
+ */
+@SuppressWarnings("unused")
+public enum BufferType {
+ HEAP {
+ @Override
+ ByteBuffer newBuffer(int size) {
+ return ByteBuffer.allocate(size);
+ }
+ },
+ DIRECT {
+ @Override
+ ByteBuffer newBuffer(int size) {
+ return ByteBuffer.allocateDirect(size);
+ }
+ };
+
+ abstract ByteBuffer newBuffer(int size);
+
+ ByteBuffer newApplicationBuffer(SSLEngine engine) {
+ return newBuffer(engine.getSession().getApplicationBufferSize());
+ }
+
+ ByteBuffer newPacketBuffer(SSLEngine engine) {
+ return newBuffer(engine.getSession().getPacketBufferSize());
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
new file mode 100644
index 0000000..c69ae39
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import org.conscrypt.TestUtils;
+
+import java.nio.ByteBuffer;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark for comparing cipher encrypt performance.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class CipherEncryptPerfTest {
+
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ public enum BufferType {
+ ARRAY,
+ HEAP_HEAP,
+ HEAP_DIRECT,
+ DIRECT_DIRECT,
+ DIRECT_HEAP
+ }
+
+ private enum MyCipherFactory implements CipherFactory {
+ JDK {
+ @Override
+ public Cipher newCipher(String transformation)
+ throws NoSuchPaddingException, NoSuchAlgorithmException {
+ return Cipher.getInstance(transformation);
+ }
+ },
+ CONSCRYPT {
+ @Override
+ public Cipher newCipher(String transformation)
+ throws NoSuchPaddingException, NoSuchAlgorithmException {
+ return Cipher.getInstance(transformation, TestUtils.getConscryptProvider());
+ }
+ };
+ }
+
+ private class Config {
+ BufferType b_bufferType;
+ CipherFactory c_provider;
+ Transformation a_tx;
+ Config(BufferType bufferType, CipherFactory cipherFactory, Transformation transformation) {
+ b_bufferType = bufferType;
+ c_provider = cipherFactory;
+ a_tx = transformation;
+ }
+ public BufferType bufferType() {
+ return b_bufferType;
+ }
+
+ public CipherFactory cipherFactory() {
+ return c_provider;
+ }
+
+ public Transformation transformation() {
+ return a_tx;
+ }
+ }
+
+ private Object[] getParams() {
+ return new Object[][] {
+ new Object[] {new Config(BufferType.ARRAY,
+ MyCipherFactory.CONSCRYPT,
+ Transformation.AES_CBC_PKCS5)},
+ new Object[] {new Config(BufferType.ARRAY,
+ MyCipherFactory.CONSCRYPT,
+ Transformation.AES_ECB_PKCS5)},
+ new Object[] {new Config(BufferType.ARRAY,
+ MyCipherFactory.CONSCRYPT,
+ Transformation.AES_GCM_NO)},
+ new Object[] {new Config(BufferType.ARRAY,
+ MyCipherFactory.CONSCRYPT,
+ Transformation.AES_GCM_SIV)},
+ };
+ }
+
+ private EncryptStrategy encryptStrategy;
+
+ @Test
+ @Parameters(method = "getParams")
+ public void encrypt(Config config) throws Exception {
+ switch (config.bufferType()) {
+ case ARRAY:
+ encryptStrategy = new ArrayStrategy(config);
+ break;
+ default:
+ encryptStrategy = new ByteBufferStrategy(config);
+ break;
+ }
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ encryptStrategy.encrypt();
+ }
+ }
+
+ private static abstract class EncryptStrategy {
+ private final Key key;
+ final Cipher cipher;
+ final int outputSize;
+
+ EncryptStrategy(Config config) throws Exception {
+ Transformation tx = config.transformation();
+ key = tx.newEncryptKey();
+ cipher = config.cipherFactory().newCipher(tx.toFormattedString());
+ initCipher();
+
+ int messageSize = messageSize(tx.toFormattedString());
+ outputSize = cipher.getOutputSize(messageSize);
+ }
+
+ final void initCipher() throws Exception {
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ }
+
+ final int messageSize(String transformation) throws Exception {
+ Cipher conscryptCipher = Cipher.getInstance(
+ transformation, TestUtils.getConscryptProvider());
+ conscryptCipher.init(Cipher.ENCRYPT_MODE, key);
+ return conscryptCipher.getBlockSize() > 0 ?
+ conscryptCipher.getBlockSize() : 128;
+ }
+
+ final byte[] newMessage() {
+ return TestUtils.newTextMessage(cipher.getBlockSize());
+ }
+
+ abstract int encrypt() throws Exception;
+ }
+
+ private static final class ArrayStrategy extends EncryptStrategy {
+ private final byte[] plainBytes;
+ private final byte[] cipherBytes;
+
+ ArrayStrategy(Config config) throws Exception {
+ super(config);
+
+ plainBytes = newMessage();
+ cipherBytes = new byte[outputSize];
+ }
+
+ @Override
+ int encrypt() throws Exception {
+ initCipher();
+ return cipher.doFinal(plainBytes, 0, plainBytes.length, cipherBytes, 0);
+ }
+ }
+
+ private static final class ByteBufferStrategy extends EncryptStrategy {
+ private final ByteBuffer input;
+ private final ByteBuffer output;
+
+ ByteBufferStrategy(Config config) throws Exception {
+ super(config);
+
+ switch (config.bufferType()) {
+ case HEAP_HEAP:
+ input = ByteBuffer.wrap(newMessage());
+ output = ByteBuffer.allocate(outputSize);
+ break;
+ case HEAP_DIRECT:
+ input = ByteBuffer.wrap(newMessage());
+ output = ByteBuffer.allocateDirect(outputSize);
+ break;
+ case DIRECT_DIRECT:
+ input = toDirect(newMessage());
+ output = ByteBuffer.allocateDirect(outputSize);
+ break;
+ case DIRECT_HEAP:
+ input = toDirect(newMessage());
+ output = ByteBuffer.allocate(outputSize);
+ break;
+ default: {
+ throw new IllegalStateException(
+ "Unexpected buffertype: " + config.bufferType());
+ }
+ }
+ }
+
+ @Override
+ int encrypt() throws Exception {
+ initCipher();
+ input.position(0);
+ output.clear();
+ return cipher.doFinal(input, output);
+ }
+
+ private static ByteBuffer toDirect(byte[] data) {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
+ buffer.put(data);
+ buffer.flip();
+ return buffer;
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamServiceWithNonexistentSettings.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherFactory.java
similarity index 60%
rename from services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamServiceWithNonexistentSettings.java
rename to apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherFactory.java
index de00175..f8a3d5f 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamServiceWithNonexistentSettings.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2017 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.
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.conscrypt;
-package com.android.server.dreams;
-
-import android.service.dreams.DreamService;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
/**
- * Dream service implementation for unit testing, where the settings activity doesn't exist.
+ * Factory for {@link Cipher} instances.
*/
-public class TestDreamServiceWithNonexistentSettings extends DreamService {
-}
+public interface CipherFactory {
+ Cipher newCipher(String transformation) throws NoSuchPaddingException, NoSuchAlgorithmException;
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java
new file mode 100644
index 0000000..1a7258a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.conscrypt.ChannelType;
+
+/**
+ * Client-side endpoint. Provides basic services for sending/receiving messages from the client
+ * socket.
+ */
+final class ClientEndpoint {
+ private final SSLSocket socket;
+ private InputStream input;
+ private OutputStream output;
+
+ ClientEndpoint(SSLSocketFactory socketFactory, ChannelType channelType, int port,
+ String[] protocols, String[] ciphers) throws IOException {
+ socket = channelType.newClientSocket(socketFactory, InetAddress.getLoopbackAddress(), port);
+ socket.setEnabledProtocols(protocols);
+ socket.setEnabledCipherSuites(ciphers);
+ }
+
+ void start() {
+ try {
+ socket.startHandshake();
+ input = socket.getInputStream();
+ output = socket.getOutputStream();
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+
+ void stop() {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ int readMessage(byte[] buffer) {
+ try {
+ int totalBytesRead = 0;
+ while (totalBytesRead < buffer.length) {
+ int remaining = buffer.length - totalBytesRead;
+ int bytesRead = input.read(buffer, totalBytesRead, remaining);
+ if (bytesRead == -1) {
+ break;
+ }
+ totalBytesRead += bytesRead;
+ }
+ return totalBytesRead;
+ } catch (SSLException e) {
+ if (e.getCause() instanceof EOFException) {
+ return -1;
+ }
+ throw new RuntimeException(e);
+ } catch (ClosedChannelException e) {
+ // Thrown for channel-based sockets. Just treat like EOF.
+ return -1;
+ } catch (SocketException e) {
+ // The socket was broken. Just treat like EOF.
+ return -1;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void sendMessage(byte[] data) {
+ try {
+ output.write(data);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void flush() {
+ try {
+ output.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
new file mode 100644
index 0000000..dd9f4eb
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import org.conscrypt.ChannelType;
+import org.conscrypt.TestUtils;
+import static org.conscrypt.TestUtils.getCommonProtocolSuites;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.security.NoSuchAlgorithmException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import android.conscrypt.ServerEndpoint.MessageProcessor;
+
+/**
+ * Benchmark for comparing performance of server socket implementations.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class ClientSocketPerfTest {
+
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Provider for the test configuration
+ */
+ private class Config {
+ EndpointFactory a_clientFactory;
+ EndpointFactory b_serverFactory;
+ int c_messageSize;
+ String d_cipher;
+ ChannelType e_channelType;
+ PerfTestProtocol f_protocol;
+ Config(EndpointFactory clientFactory,
+ EndpointFactory serverFactory,
+ int messageSize,
+ String cipher,
+ ChannelType channelType,
+ PerfTestProtocol protocol) {
+ a_clientFactory = clientFactory;
+ b_serverFactory = serverFactory;
+ c_messageSize = messageSize;
+ d_cipher = cipher;
+ e_channelType = channelType;
+ f_protocol = protocol;
+ }
+ public EndpointFactory clientFactory() {
+ return a_clientFactory;
+ }
+
+ public EndpointFactory serverFactory() {
+ return b_serverFactory;
+ }
+
+ public int messageSize() {
+ return c_messageSize;
+ }
+
+ public String cipher() {
+ return d_cipher;
+ }
+
+ public ChannelType channelType() {
+ return e_channelType;
+ }
+
+ public PerfTestProtocol protocol() {
+ return f_protocol;
+ }
+ }
+
+ private Object[] getParams() {
+ return new Object[][] {
+ new Object[] {new Config(
+ EndpointFactory.CONSCRYPT,
+ EndpointFactory.CONSCRYPT,
+ 64,
+ "AES128-GCM",
+ ChannelType.CHANNEL,
+ PerfTestProtocol.TLSv13)},
+ };
+ }
+
+
+ private ClientEndpoint client;
+ private ServerEndpoint server;
+ private byte[] message;
+ private ExecutorService executor;
+ private Future<?> sendingFuture;
+ private volatile boolean stopping;
+
+ private static final AtomicLong bytesCounter = new AtomicLong();
+ private AtomicBoolean recording = new AtomicBoolean();
+
+ private void setup(Config config) throws Exception {
+ message = newTextMessage(512);
+
+ // Always use the same server for consistency across the benchmarks.
+ server = config.serverFactory().newServer(
+ ChannelType.CHANNEL, config.messageSize(), config.protocol().getProtocols(),
+ ciphers(config));
+
+ server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
+ @Override
+ public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+ if (recording.get()) {
+ // Server received a message, increment the count.
+ bytesCounter.addAndGet(numBytes);
+ }
+ }
+ });
+ Future<?> connectedFuture = server.start();
+
+ client = config.clientFactory().newClient(
+ config.channelType(), server.port(), config.protocol().getProtocols(), ciphers(config));
+ client.start();
+
+ // Wait for the initial connection to complete.
+ connectedFuture.get(5, TimeUnit.SECONDS);
+
+ executor = Executors.newSingleThreadExecutor();
+ sendingFuture = executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread thread = Thread.currentThread();
+ while (!stopping && !thread.isInterrupted()) {
+ client.sendMessage(message);
+ }
+ } finally {
+ client.flush();
+ }
+ }
+ });
+ }
+
+ void close() throws Exception {
+ stopping = true;
+
+ // Wait for the sending thread to stop.
+ sendingFuture.get(5, TimeUnit.SECONDS);
+
+ client.stop();
+ server.stop();
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Simple benchmark for the amount of time to send a given number of messages
+ */
+ @Test
+ @Parameters(method = "getParams")
+ public void time(Config config) throws Exception {
+ reset();
+ setup(config);
+ recording.set(true);
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ while (bytesCounter.get() < config.messageSize()) {
+ }
+ bytesCounter.set(0);
+ }
+ recording.set(false);
+ close();
+ }
+
+ void reset() {
+ stopping = false;
+ bytesCounter.set(0);
+ }
+
+ private String[] ciphers(Config config) {
+ return new String[] {config.cipher()};
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
new file mode 100644
index 0000000..0655f45
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import org.conscrypt.ChannelType;
+import org.conscrypt.TestUtils;
+import java.io.IOException;
+import java.security.Provider;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Utility for creating test client and server instances.
+ */
+public enum EndpointFactory {
+ CONSCRYPT(newConscryptFactories(false)),
+ CONSCRYPT_ENGINE(newConscryptFactories(true));
+
+ private final Factories factories;
+
+ EndpointFactory(Factories factories) {
+ this.factories = factories;
+ }
+
+ public ClientEndpoint newClient(ChannelType channelType, int port, String[] protocols,
+ String[] ciphers) throws IOException {
+ return new ClientEndpoint(
+ factories.clientFactory, channelType, port, protocols, ciphers);
+ }
+
+ public ServerEndpoint newServer(ChannelType channelType, int messageSize,
+ String[] protocols, String[] ciphers) throws IOException {
+ return new ServerEndpoint(factories.serverFactory, factories.serverSocketFactory,
+ channelType, messageSize, protocols, ciphers);
+ }
+
+ private static final class Factories {
+ final SSLSocketFactory clientFactory;
+ final SSLSocketFactory serverFactory;
+ final SSLServerSocketFactory serverSocketFactory;
+
+ private Factories(SSLSocketFactory clientFactory, SSLSocketFactory serverFactory,
+ SSLServerSocketFactory serverSocketFactory) {
+ this.clientFactory = clientFactory;
+ this.serverFactory = serverFactory;
+ this.serverSocketFactory = serverSocketFactory;
+ }
+ }
+
+ private static Factories newConscryptFactories(boolean useEngineSocket) {
+ Provider provider = TestUtils.getConscryptProvider();
+ SSLContext clientContext = TestUtils.newClientSslContext(provider);
+ SSLContext serverContext = TestUtils.newServerSslContext(provider);
+ final SSLSocketFactory clientFactory = clientContext.getSocketFactory();
+ final SSLSocketFactory serverFactory = serverContext.getSocketFactory();
+ final SSLServerSocketFactory serverSocketFactory = serverContext.getServerSocketFactory();
+ TestUtils.setUseEngineSocket(clientFactory, useEngineSocket);
+ TestUtils.setUseEngineSocket(serverFactory, useEngineSocket);
+ TestUtils.setUseEngineSocket(serverSocketFactory, useEngineSocket);
+ return new Factories(clientFactory, serverFactory, serverSocketFactory);
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/OWNERS b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/OWNERS
new file mode 100644
index 0000000..7efabfd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 24949
+include platform/libcore:/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/PerfTestProtocol.java
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to apct-tests/perftests/core/src/android/conscrypt/conscrypt/PerfTestProtocol.java
index d8af3fa..4defe71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/PerfTestProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,8 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package android.conscrypt;
-import com.android.systemui.kosmos.Kosmos
+public enum PerfTestProtocol {
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+ TLSv13("TLSv1.3"),
+ TLSv12("TLSv1.2");
+
+ private final String[] protocols;
+
+ PerfTestProtocol(String... protocols) {
+ this.protocols = protocols;
+ }
+
+ public String[] getProtocols() {
+ return protocols.clone();
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
new file mode 100644
index 0000000..3631c3f
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.conscrypt.ChannelType;
+
+/**
+ * A simple socket-based test server.
+ */
+final class ServerEndpoint {
+ /**
+ * A processor for receipt of a single message.
+ */
+ public interface MessageProcessor {
+ void processMessage(byte[] message, int numBytes, OutputStream os);
+ }
+
+ /**
+ * A {@link MessageProcessor} that simply echos back the received message to the client.
+ */
+ public static final class EchoProcessor implements MessageProcessor {
+ @Override
+ public void processMessage(byte[] message, int numBytes, OutputStream os) {
+ try {
+ os.write(message, 0, numBytes);
+ os.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private final ServerSocket serverSocket;
+ private final ChannelType channelType;
+ private final SSLSocketFactory socketFactory;
+ private final int messageSize;
+ private final String[] protocols;
+ private final String[] cipherSuites;
+ private final byte[] buffer;
+ private SSLSocket socket;
+ private ExecutorService executor;
+ private InputStream inputStream;
+ private OutputStream outputStream;
+ private volatile boolean stopping;
+ private volatile MessageProcessor messageProcessor = new EchoProcessor();
+ private volatile Future<?> processFuture;
+
+ ServerEndpoint(SSLSocketFactory socketFactory, SSLServerSocketFactory serverSocketFactory,
+ ChannelType channelType, int messageSize, String[] protocols,
+ String[] cipherSuites) throws IOException {
+ this.serverSocket = channelType.newServerSocket(serverSocketFactory);
+ this.socketFactory = socketFactory;
+ this.channelType = channelType;
+ this.messageSize = messageSize;
+ this.protocols = protocols;
+ this.cipherSuites = cipherSuites;
+ buffer = new byte[messageSize];
+ }
+
+ void setMessageProcessor(MessageProcessor messageProcessor) {
+ this.messageProcessor = messageProcessor;
+ }
+
+ Future<?> start() throws IOException {
+ executor = Executors.newSingleThreadExecutor();
+ return executor.submit(new AcceptTask());
+ }
+
+ void stop() {
+ try {
+ stopping = true;
+
+ if (socket != null) {
+ socket.close();
+ socket = null;
+ }
+
+ if (processFuture != null) {
+ processFuture.get(5, TimeUnit.SECONDS);
+ }
+
+ serverSocket.close();
+
+ if (executor != null) {
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ executor = null;
+ }
+ } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int port() {
+ return serverSocket.getLocalPort();
+ }
+
+ private final class AcceptTask implements Runnable {
+ @Override
+ public void run() {
+ try {
+ if (stopping) {
+ return;
+ }
+ socket = channelType.accept(serverSocket, socketFactory);
+ socket.setEnabledProtocols(protocols);
+ socket.setEnabledCipherSuites(cipherSuites);
+
+ socket.startHandshake();
+
+ inputStream = socket.getInputStream();
+ outputStream = socket.getOutputStream();
+
+ if (stopping) {
+ return;
+ }
+ processFuture = executor.submit(new ProcessTask());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private final class ProcessTask implements Runnable {
+ @Override
+ public void run() {
+ try {
+ Thread thread = Thread.currentThread();
+ while (!stopping && !thread.isInterrupted()) {
+ int bytesRead = readMessage();
+ if (!stopping && !thread.isInterrupted()) {
+ messageProcessor.processMessage(buffer, bytesRead, outputStream);
+ }
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private int readMessage() throws IOException {
+ int totalBytesRead = 0;
+ while (!stopping && totalBytesRead < messageSize) {
+ try {
+ int remaining = messageSize - totalBytesRead;
+ int bytesRead = inputStream.read(buffer, totalBytesRead, remaining);
+ if (bytesRead == -1) {
+ break;
+ }
+ totalBytesRead += bytesRead;
+ } catch (SSLException e) {
+ if (e.getCause() instanceof EOFException) {
+ break;
+ }
+ throw e;
+ } catch (ClosedChannelException e) {
+ // Thrown for channel-based sockets. Just treat like EOF.
+ break;
+ } catch (SocketException e) {
+ // The socket was broken. Just treat like EOF.
+ break;
+ }
+ }
+ return totalBytesRead;
+ }
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
new file mode 100644
index 0000000..ba2a65a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import org.conscrypt.ChannelType;
+import static org.conscrypt.TestUtils.getCommonProtocolSuites;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import android.conscrypt.ServerEndpoint.MessageProcessor;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark for comparing performance of server socket implementations.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class ServerSocketPerfTest {
+
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Provider for the benchmark configuration
+ */
+ private class Config {
+ EndpointFactory a_clientFactory;
+ EndpointFactory b_serverFactory;
+ int c_messageSize;
+ String d_cipher;
+ ChannelType e_channelType;
+ Config(EndpointFactory clientFactory,
+ EndpointFactory serverFactory,
+ int messageSize,
+ String cipher,
+ ChannelType channelType) {
+ a_clientFactory = clientFactory;
+ b_serverFactory = serverFactory;
+ c_messageSize = messageSize;
+ d_cipher = cipher;
+ e_channelType = channelType;
+ }
+ public EndpointFactory clientFactory() {
+ return a_clientFactory;
+ }
+
+ public EndpointFactory serverFactory() {
+ return b_serverFactory;
+ }
+
+ public int messageSize() {
+ return c_messageSize;
+ }
+
+ public String cipher() {
+ return d_cipher;
+ }
+
+ public ChannelType channelType() {
+ return e_channelType;
+ }
+ }
+
+ private Object[] getParams() {
+ return new Object[][] {
+ new Object[] {new Config(
+ EndpointFactory.CONSCRYPT,
+ EndpointFactory.CONSCRYPT,
+ 64,
+ "AES128-GCM",
+ ChannelType.CHANNEL)},
+ };
+ }
+
+ private ClientEndpoint client;
+ private ServerEndpoint server;
+ private ExecutorService executor;
+ private Future<?> receivingFuture;
+ private volatile boolean stopping;
+ private static final AtomicLong bytesCounter = new AtomicLong();
+ private AtomicBoolean recording = new AtomicBoolean();
+
+ private void setup(final Config config) throws Exception {
+ recording.set(false);
+
+ byte[] message = newTextMessage(config.messageSize());
+
+ final ChannelType channelType = config.channelType();
+
+ server = config.serverFactory().newServer(
+ channelType, config.messageSize(), getCommonProtocolSuites(), ciphers(config));
+ server.setMessageProcessor(new MessageProcessor() {
+ @Override
+ public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+ try {
+ try {
+ while (!stopping) {
+ os.write(inMessage, 0, numBytes);
+ }
+ } finally {
+ os.flush();
+ }
+ } catch (SocketException e) {
+ // Just ignore.
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ Future<?> connectedFuture = server.start();
+
+ // Always use the same client for consistency across the benchmarks.
+ client = config.clientFactory().newClient(
+ ChannelType.CHANNEL, server.port(), getCommonProtocolSuites(), ciphers(config));
+ client.start();
+
+ // Wait for the initial connection to complete.
+ connectedFuture.get(5, TimeUnit.SECONDS);
+
+ // Start the server-side streaming by sending a message to the server.
+ client.sendMessage(message);
+ client.flush();
+
+ executor = Executors.newSingleThreadExecutor();
+ receivingFuture = executor.submit(new Runnable() {
+ @Override
+ public void run() {
+ Thread thread = Thread.currentThread();
+ byte[] buffer = new byte[config.messageSize()];
+ while (!stopping && !thread.isInterrupted()) {
+ int numBytes = client.readMessage(buffer);
+ if (numBytes < 0) {
+ return;
+ }
+ assertEquals(config.messageSize(), numBytes);
+
+ // Increment the message counter if we're recording.
+ if (recording.get()) {
+ bytesCounter.addAndGet(numBytes);
+ }
+ }
+ }
+ });
+ }
+
+ void close() throws Exception {
+ stopping = true;
+ // Stop and wait for sending to complete.
+ server.stop();
+ client.stop();
+ executor.shutdown();
+ receivingFuture.get(5, TimeUnit.SECONDS);
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ @Test
+ @Parameters(method = "getParams")
+ public void throughput(Config config) throws Exception {
+ setup(config);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ recording.set(true);
+ while (bytesCounter.get() < config.messageSize()) {
+ }
+ bytesCounter.set(0);
+ recording.set(false);
+ }
+ close();
+ }
+
+ private String[] ciphers(Config config) {
+ return new String[] {config.cipher()};
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
new file mode 100644
index 0000000..78fe732
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.KeyGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/**
+ * Supported cipher transformations.
+ */
+@SuppressWarnings({"ImmutableEnumChecker", "unused"})
+public enum Transformation {
+ AES_CBC_PKCS5("AES", "CBC", "PKCS5Padding", new AesKeyGen()),
+ AES_ECB_PKCS5("AES", "ECB", "PKCS5Padding", new AesKeyGen()),
+ AES_GCM_NO("AES", "GCM", "NoPadding", new AesKeyGen()),
+ AES_GCM_SIV("AES", "GCM_SIV", "NoPadding", new AesKeyGen()),
+ RSA_ECB_PKCS1("RSA", "ECB", "PKCS1Padding", new RsaKeyGen());
+
+ Transformation(String algorithm, String mode, String padding, KeyGen keyGen) {
+ this.algorithm = algorithm;
+ this.mode = mode;
+ this.padding = padding;
+ this.keyGen = keyGen;
+ }
+
+ final String algorithm;
+ final String mode;
+ final String padding;
+ final KeyGen keyGen;
+
+ String toFormattedString() {
+ return algorithm + "/" + mode + "/" + padding;
+ }
+
+ Key newEncryptKey() {
+ return keyGen.newEncryptKey();
+ }
+
+ private interface KeyGen { Key newEncryptKey(); }
+
+ private static final class RsaKeyGen implements KeyGen {
+ @Override
+ public Key newEncryptKey() {
+ try {
+ // Use Bouncy castle
+ KeyPairGenerator generator =
+ KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
+ generator.initialize(2048);
+ KeyPair pair = generator.generateKeyPair();
+ return pair.getPublic();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private static final class AesKeyGen implements KeyGen {
+ @Override
+ public Key newEncryptKey() {
+ try {
+ // Just use the JDK's provider.
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+ keyGen.init(256);
+ return keyGen.generateKey();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index d80d3c8..b955032 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -18,11 +18,8 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
-
import android.app.Activity;
import android.content.Context;
-import android.os.Bundle;
import android.os.RemoteException;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
@@ -131,7 +128,6 @@
final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
final InsetsState mOutInsetsState = new InsetsState();
final InsetsSourceControl.Array mOutControls = new InsetsSourceControl.Array();
- final Bundle mOutBundle = windowSessionRelayoutInfo() ? null : new Bundle();
final WindowRelayoutResult mOutRelayoutResult;
final IWindow mWindow;
final View mView;
@@ -152,26 +148,16 @@
mHeight = mView.getMeasuredHeight();
mOutSurfaceControl = mView.getViewRootImpl().getSurfaceControl();
mViewVisibility = visibilitySupplier;
- mOutRelayoutResult = windowSessionRelayoutInfo()
- ? new WindowRelayoutResult(mOutFrames, mOutMergedConfiguration,
- mOutSurfaceControl, mOutInsetsState, mOutControls)
- : null;
+ mOutRelayoutResult = new WindowRelayoutResult(mOutFrames, mOutMergedConfiguration,
+ mOutSurfaceControl, mOutInsetsState, mOutControls);
}
void runBenchmark(BenchmarkState state) throws RemoteException {
final IWindowSession session = WindowManagerGlobal.getWindowSession();
while (state.keepRunning()) {
mRelayoutSeq++;
- if (windowSessionRelayoutInfo()) {
- session.relayout(mWindow, mParams, mWidth, mHeight,
- mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */,
- mOutRelayoutResult);
- } else {
- session.relayoutLegacy(mWindow, mParams, mWidth, mHeight,
- mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */,
- mOutFrames, mOutMergedConfiguration, mOutSurfaceControl,
- mOutInsetsState, mOutControls, mOutBundle);
- }
+ session.relayout(mWindow, mParams, mWidth, mHeight, mViewVisibility.getAsInt(),
+ mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */, mOutRelayoutResult);
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index b982d12..dfa7206 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -4925,7 +4925,6 @@
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
sdFilter.addAction(Intent.ACTION_USER_STOPPED);
if (mStartUserBeforeScheduledAlarms) {
- sdFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
sdFilter.addAction(Intent.ACTION_USER_REMOVED);
}
sdFilter.addAction(Intent.ACTION_UID_REMOVED);
@@ -4958,14 +4957,6 @@
mTemporaryQuotaReserve.removeForUser(userHandle);
}
return;
- case Intent.ACTION_LOCKED_BOOT_COMPLETED:
- final int handle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (handle >= 0) {
- if (mStartUserBeforeScheduledAlarms) {
- mUserWakeupStore.onUserStarted(handle);
- }
- }
- return;
case Intent.ACTION_USER_REMOVED:
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (user >= 0) {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
index 7fc630c..dc5e341 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
@@ -98,12 +98,7 @@
*/
@GuardedBy("mUserWakeupLock")
private final SparseLongArray mUserStarts = new SparseLongArray();
- /**
- * A list of users that are in a phase after they have been started but before alarms were
- * initialized.
- */
- @GuardedBy("mUserWakeupLock")
- private final SparseLongArray mStartingUsers = new SparseLongArray();
+
private Executor mBackgroundExecutor;
private static final File USER_WAKEUP_DIR = new File(Environment.getDataSystemDirectory(),
ROOT_DIR_NAME);
@@ -124,9 +119,6 @@
*/
public void addUserWakeup(int userId, long alarmTime) {
synchronized (mUserWakeupLock) {
- // This should not be needed, but if an app in the user is scheduling an alarm clock, we
- // consider the user start complete. There is a dedicated removal when user is started.
- mStartingUsers.delete(userId);
mUserStarts.put(userId, alarmTime - BUFFER_TIME_MS + getUserWakeupOffset());
}
updateUserListFile();
@@ -192,23 +184,10 @@
}
/**
- * Move user from wakeup list to starting user list.
+ * Remove scheduled user wakeup from the list when it is started.
*/
public void onUserStarting(int userId) {
- synchronized (mUserWakeupLock) {
- final long wakeup = getWakeupTimeForUser(userId);
- if (wakeup >= 0) {
- mStartingUsers.put(userId, wakeup);
- mUserStarts.delete(userId);
- }
- }
- }
-
- /**
- * Remove userId from starting user list once start is complete.
- */
- public void onUserStarted(int userId) {
- if (deleteWakeupFromStartingUsers(userId)) {
+ if (deleteWakeupFromUserStarts(userId)) {
updateUserListFile();
}
}
@@ -217,7 +196,7 @@
* Remove userId from the store when the user is removed.
*/
public void onUserRemoved(int userId) {
- if (deleteWakeupFromUserStarts(userId) || deleteWakeupFromStartingUsers(userId)) {
+ if (deleteWakeupFromUserStarts(userId)) {
updateUserListFile();
}
}
@@ -227,29 +206,14 @@
* @return true if an entry is found and the list of wakeups changes.
*/
private boolean deleteWakeupFromUserStarts(int userId) {
- int index;
synchronized (mUserWakeupLock) {
- index = mUserStarts.indexOfKey(userId);
+ final int index = mUserStarts.indexOfKey(userId);
if (index >= 0) {
mUserStarts.removeAt(index);
+ return true;
}
+ return false;
}
- return index >= 0;
- }
-
- /**
- * Remove wakeup for a given userId from mStartingUsers.
- * @return true if an entry is found and the list of wakeups changes.
- */
- private boolean deleteWakeupFromStartingUsers(int userId) {
- int index;
- synchronized (mUserWakeupLock) {
- index = mStartingUsers.indexOfKey(userId);
- if (index >= 0) {
- mStartingUsers.removeAt(index);
- }
- }
- return index >= 0;
}
/**
@@ -299,9 +263,6 @@
for (int i = 0; i < mUserStarts.size(); i++) {
listOfUsers.add(new Pair<>(mUserStarts.keyAt(i), mUserStarts.valueAt(i)));
}
- for (int i = 0; i < mStartingUsers.size(); i++) {
- listOfUsers.add(new Pair<>(mStartingUsers.keyAt(i), mStartingUsers.valueAt(i)));
- }
}
Collections.sort(listOfUsers, Comparator.comparingLong(pair -> pair.second));
for (int i = 0; i < listOfUsers.size(); i++) {
@@ -329,7 +290,6 @@
}
synchronized (mUserWakeupLock) {
mUserStarts.clear();
- mStartingUsers.clear();
}
try (FileInputStream fis = userWakeupFile.openRead()) {
final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
@@ -396,14 +356,6 @@
TimeUtils.formatDuration(mUserStarts.valueAt(i), nowELAPSED, pw);
pw.println();
}
- pw.println(mStartingUsers.size() + " starting users: ");
- for (int i = 0; i < mStartingUsers.size(); i++) {
- pw.print("UserId: ");
- pw.print(mStartingUsers.keyAt(i));
- pw.print(", userStartTime: ");
- TimeUtils.formatDuration(mStartingUsers.valueAt(i), nowELAPSED, pw);
- pw.println();
- }
pw.decreaseIndent();
}
}
diff --git a/api/Android.bp b/api/Android.bp
index 3fa9c60..cd1997c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -157,6 +157,7 @@
genrule {
name: "frameworks-base-api-system-current-compat",
srcs: [
+ ":android.api.public.latest",
":android.api.system.latest",
":android-incompatibilities.api.system.latest",
":frameworks-base-api-current.txt",
@@ -165,33 +166,35 @@
out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
+ "--check-compatibility:api:released $(location :android.api.public.latest) " +
"--check-compatibility:api:released $(location :android.api.system.latest) " +
- "--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " +
"--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+ "$(location :frameworks-base-api-current.txt) " +
"$(location :frameworks-base-api-system-current.txt)",
}
genrule {
name: "frameworks-base-api-module-lib-current-compat",
srcs: [
+ ":android.api.public.latest",
+ ":android.api.system.latest",
":android.api.module-lib.latest",
":android-incompatibilities.api.module-lib.latest",
":frameworks-base-api-current.txt",
+ ":frameworks-base-api-system-current.txt",
":frameworks-base-api-module-lib-current.txt",
],
out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
+ "--check-compatibility:api:released $(location :android.api.public.latest) " +
+ "--check-compatibility:api:released $(location :android.api.system.latest) " +
"--check-compatibility:api:released $(location :android.api.module-lib.latest) " +
- // Note: having "public" be the base of module-lib is not perfect -- it should
- // ideally be a merged public+system (which metalava is not currently able to generate).
- // This "base" will help when migrating from MODULE_LIBS -> public, but not when
- // migrating from MODULE_LIBS -> system (where it needs to instead be listed as
- // an incompatibility).
- "--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " +
"--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+ "$(location :frameworks-base-api-current.txt) " +
+ "$(location :frameworks-base-api-system-current.txt) " +
"$(location :frameworks-base-api-module-lib-current.txt)",
}
@@ -373,7 +376,6 @@
high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
annotations_enabled: true,
- previous_api: ":android.api.public.latest",
merge_annotations_dirs: ["metalava-manual"],
defaults_visibility: ["//frameworks/base/api"],
visibility: [
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 6cfd2e0..12820f9 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -38,6 +38,9 @@
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
],
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.public.latest",
check_api: {
current: {
api_file: ":non-updatable-current.txt",
@@ -118,6 +121,9 @@
"module-classpath-stubs-defaults",
],
flags: priv_apps,
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.system.latest",
check_api: {
current: {
api_file: ":non-updatable-system-current.txt",
@@ -178,6 +184,9 @@
"module-classpath-stubs-defaults",
],
flags: test + priv_apps_in_stubs,
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.test.latest",
check_api: {
current: {
api_file: ":non-updatable-test-current.txt",
@@ -257,6 +266,9 @@
"module-classpath-stubs-defaults",
],
flags: priv_apps_in_stubs + module_libs,
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.module-lib.latest",
check_api: {
current: {
api_file: ":non-updatable-module-lib-current.txt",
@@ -571,6 +583,9 @@
],
defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_stubs_current.from-text",
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.public.latest",
}
java_api_library {
@@ -582,6 +597,9 @@
],
defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_system_stubs_current.from-text",
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.system.latest",
}
java_api_library {
@@ -594,6 +612,9 @@
],
defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_stubs_current.from-text",
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.test.latest",
}
java_api_library {
@@ -606,6 +627,9 @@
],
defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
+ // Use full Android API not just the non-updatable API as the latter is incomplete
+ // and can result in incorrect behavior.
+ previous_api: ":android.api.combined.module-lib.latest",
}
// This module generates a stub jar that is a union of the test and module lib
@@ -623,6 +647,8 @@
defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
+ // No need to specify previous_api as this is not used for compiling against.
+
// This module is only used for hiddenapi, and other modules should not
// depend on this module.
visibility: ["//visibility:private"],
@@ -922,6 +948,7 @@
"i18n.module.public.api.stubs.source.system.api.contribution",
"i18n.module.public.api.stubs.source.module_lib.api.contribution",
],
+ previous_api: ":android.api.combined.module-lib.latest",
}
// Java API library definitions per API surface
diff --git a/api/api.go b/api/api.go
index 449fac6..d4db49e 100644
--- a/api/api.go
+++ b/api/api.go
@@ -478,7 +478,7 @@
props.Api_contributions = transformArray(
modules, "", fmt.Sprintf(".stubs.source%s.api.contribution", apiSuffix))
props.Defaults_visibility = []string{"//visibility:public"}
- props.Previous_api = proptools.StringPtr(":android.api.public.latest")
+ props.Previous_api = proptools.StringPtr(":android.api.combined." + sdkKind.String() + ".latest")
ctx.CreateModule(java.DefaultsFactory, &props)
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 5eeb299..44a6c6b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2456,7 +2456,7 @@
field public static final int config_longAnimTime = 17694722; // 0x10e0002
field public static final int config_mediumAnimTime = 17694721; // 0x10e0001
field public static final int config_shortAnimTime = 17694720; // 0x10e0000
- field public static final int status_bar_notification_info_maxnum = 17694723; // 0x10e0003
+ field @Deprecated public static final int status_bar_notification_info_maxnum = 17694723; // 0x10e0003
}
public static final class R.interpolator {
@@ -2550,7 +2550,7 @@
field public static final int search_go = 17039372; // 0x104000c
field public static final int selectAll = 17039373; // 0x104000d
field public static final int selectTextMode = 17039382; // 0x1040016
- field public static final int status_bar_notification_info_overflow = 17039383; // 0x1040017
+ field @Deprecated public static final int status_bar_notification_info_overflow = 17039383; // 0x1040017
field public static final int unknownName = 17039374; // 0x104000e
field public static final int untitled = 17039375; // 0x104000f
field @Deprecated public static final int yes = 17039379; // 0x1040013
@@ -3453,6 +3453,8 @@
field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
+ field @FlaggedApi("android.view.accessibility.global_action_media_play_pause") public static final int GLOBAL_ACTION_MEDIA_PLAY_PAUSE = 22; // 0x16
+ field @FlaggedApi("android.view.accessibility.global_action_menu") public static final int GLOBAL_ACTION_MENU = 21; // 0x15
field public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; // 0x4
field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
@@ -6699,7 +6701,7 @@
method @NonNull public android.app.Notification.Builder setExtras(android.os.Bundle);
method @NonNull public android.app.Notification.Builder setFlag(int, boolean);
method @NonNull public android.app.Notification.Builder setForegroundServiceBehavior(int);
- method @NonNull public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.USE_FULL_SCREEN_INTENT) public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
method @NonNull public android.app.Notification.Builder setGroup(String);
method @NonNull public android.app.Notification.Builder setGroupAlertBehavior(int);
method @NonNull public android.app.Notification.Builder setGroupSummary(boolean);
@@ -9824,6 +9826,7 @@
method public void onAssociationPending(@NonNull android.content.IntentSender);
method @Deprecated public void onDeviceFound(@NonNull android.content.IntentSender);
method public abstract void onFailure(@Nullable CharSequence);
+ method @FlaggedApi("android.companion.association_failure_code") public void onFailure(int);
}
public abstract class CompanionDeviceService extends android.app.Service {
@@ -11333,7 +11336,7 @@
field public static final String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
field public static final String CATEGORY_VOICE = "android.intent.category.VOICE";
field public static final String CATEGORY_VR_HOME = "android.intent.category.VR_HOME";
- field @FlaggedApi("android.service.chooser.chooser_album_text") public static final int CHOOSER_CONTENT_TYPE_ALBUM = 1; // 0x1
+ field public static final int CHOOSER_CONTENT_TYPE_ALBUM = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.content.Intent> CREATOR;
field public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
@@ -11356,7 +11359,7 @@
field public static final String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
field public static final String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
field @FlaggedApi("android.service.chooser.chooser_payload_toggling") public static final String EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI = "android.intent.extra.CHOOSER_ADDITIONAL_CONTENT_URI";
- field @FlaggedApi("android.service.chooser.chooser_album_text") public static final String EXTRA_CHOOSER_CONTENT_TYPE_HINT = "android.intent.extra.CHOOSER_CONTENT_TYPE_HINT";
+ field public static final String EXTRA_CHOOSER_CONTENT_TYPE_HINT = "android.intent.extra.CHOOSER_CONTENT_TYPE_HINT";
field public static final String EXTRA_CHOOSER_CUSTOM_ACTIONS = "android.intent.extra.CHOOSER_CUSTOM_ACTIONS";
field @FlaggedApi("android.service.chooser.chooser_payload_toggling") public static final String EXTRA_CHOOSER_FOCUSED_ITEM_POSITION = "android.intent.extra.CHOOSER_FOCUSED_ITEM_POSITION";
field public static final String EXTRA_CHOOSER_MODIFY_SHARE_ACTION = "android.intent.extra.CHOOSER_MODIFY_SHARE_ACTION";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 50d97cf..b3c471c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -105,6 +105,7 @@
field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
field @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") public static final String CAMERA_PRIVACY_ALLOWLIST = "android.permission.CAMERA_PRIVACY_ALLOWLIST";
field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
+ field @FlaggedApi("android.os.allow_consentless_bugreport_delegated_consent") public static final String CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT";
field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD";
field public static final String CAPTURE_MEDIA_OUTPUT = "android.permission.CAPTURE_MEDIA_OUTPUT";
field public static final String CAPTURE_TUNER_AUDIO_INPUT = "android.permission.CAPTURE_TUNER_AUDIO_INPUT";
@@ -3475,6 +3476,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.input.VirtualMouseConfig);
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualNavigationTouchpad createVirtualNavigationTouchpad(@NonNull android.hardware.input.VirtualNavigationTouchpadConfig);
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull android.hardware.input.VirtualRotaryEncoderConfig);
method @FlaggedApi("android.companion.virtual.flags.virtual_stylus") @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualStylus createVirtualStylus(@NonNull android.hardware.input.VirtualStylusConfig);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -5761,6 +5763,37 @@
method @NonNull public android.hardware.input.VirtualNavigationTouchpadConfig build();
}
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public class VirtualRotaryEncoder implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualRotaryEncoderScrollEvent);
+ }
+
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public final class VirtualRotaryEncoderConfig extends android.hardware.input.VirtualInputDeviceConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualRotaryEncoderConfig> CREATOR;
+ }
+
+ public static final class VirtualRotaryEncoderConfig.Builder extends android.hardware.input.VirtualInputDeviceConfig.Builder<android.hardware.input.VirtualRotaryEncoderConfig.Builder> {
+ ctor public VirtualRotaryEncoderConfig.Builder();
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderConfig build();
+ }
+
+ @FlaggedApi("android.companion.virtualdevice.flags.virtual_rotary") public final class VirtualRotaryEncoderScrollEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getEventTimeNanos();
+ method @FloatRange(from=-1.0F, to=1.0f) public float getScrollAmount();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.VirtualRotaryEncoderScrollEvent> CREATOR;
+ }
+
+ public static final class VirtualRotaryEncoderScrollEvent.Builder {
+ ctor public VirtualRotaryEncoderScrollEvent.Builder();
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent build();
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent.Builder setEventTimeNanos(long);
+ method @NonNull public android.hardware.input.VirtualRotaryEncoderScrollEvent.Builder setScrollAmount(@FloatRange(from=-1.0F, to=1.0f) float);
+ }
+
@FlaggedApi("android.companion.virtual.flags.virtual_stylus") public class VirtualStylus implements java.io.Closeable {
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualStylusButtonEvent);
@@ -11471,6 +11504,19 @@
}
+package android.os.vibrator.persistence {
+
+ @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration {
+ method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator);
+ }
+
+ @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class VibrationXmlParser {
+ method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.vibrator.persistence.ParsedVibration parse(@NonNull java.io.InputStream) throws java.io.IOException;
+ method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.InputStream) throws java.io.IOException;
+ }
+
+}
+
package android.permission {
public final class AdminPermissionControlParams implements android.os.Parcelable {
@@ -12886,7 +12932,13 @@
field public static final String KEY_SENSITIVE_CONTENT = "key_sensitive_content";
field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
field public static final String KEY_TEXT_REPLIES = "key_text_replies";
+ field @FlaggedApi("android.service.notification.notification_classification") public static final String KEY_TYPE = "key_type";
field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
+ field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_CONTENT_RECOMMENDATION = 4; // 0x4
+ field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_NEWS = 3; // 0x3
+ field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_OTHER = 0; // 0x0
+ field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_PROMOTION = 1; // 0x1
+ field @FlaggedApi("android.service.notification.notification_classification") public static final int TYPE_SOCIAL_MEDIA = 2; // 0x2
}
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c2f960f..1352465 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -379,6 +379,10 @@
method public void setImportantConversation(boolean);
method public void setOriginalImportance(int);
method public void setUserVisibleTaskShown(boolean);
+ field @FlaggedApi("android.service.notification.notification_classification") public static final String NEWS_ID = "android.app.news";
+ field @FlaggedApi("android.service.notification.notification_classification") public static final String PROMOTIONS_ID = "android.app.promotions";
+ field @FlaggedApi("android.service.notification.notification_classification") public static final String RECS_ID = "android.app.recs";
+ field @FlaggedApi("android.service.notification.notification_classification") public static final String SOCIAL_MEDIA_ID = "android.app.social";
}
public final class NotificationChannelGroup implements android.os.Parcelable {
@@ -2757,21 +2761,24 @@
package android.os.vibrator.persistence {
- public class ParsedVibration {
- method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects();
- method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator);
+ @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class ParsedVibration {
+ ctor public ParsedVibration(@NonNull java.util.List<android.os.VibrationEffect>);
+ method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator);
}
- public final class VibrationXmlParser {
- method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException;
- method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException;
+ @FlaggedApi("android.os.vibrator.vibration_xml_apis") public final class VibrationXmlParser {
+ method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.vibrator.persistence.ParsedVibration parse(@NonNull java.io.InputStream) throws java.io.IOException;
+ method @FlaggedApi("android.os.vibrator.vibration_xml_apis") @NonNull public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.InputStream) throws java.io.IOException;
+ }
+
+ public static final class VibrationXmlParser.ParseFailedException extends java.io.IOException {
}
public final class VibrationXmlSerializer {
- method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException;
+ method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException;
}
- public static final class VibrationXmlSerializer.SerializationFailedException extends java.lang.RuntimeException {
+ public static final class VibrationXmlSerializer.SerializationFailedException extends java.io.IOException {
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index fd9600c..19ffc17f 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -67,6 +67,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.Flags;
import android.view.inputmethod.EditorInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -625,6 +626,18 @@
*/
public static final int GLOBAL_ACTION_DPAD_CENTER = 20;
+ /**
+ * Action to trigger menu key event.
+ */
+ @FlaggedApi(Flags.FLAG_GLOBAL_ACTION_MENU)
+ public static final int GLOBAL_ACTION_MENU = 21;
+
+ /**
+ * Action to trigger media play/pause key event.
+ */
+ @FlaggedApi(Flags.FLAG_GLOBAL_ACTION_MEDIA_PLAY_PAUSE)
+ public static final int GLOBAL_ACTION_MEDIA_PLAY_PAUSE = 22;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 497d47a..87acbbf 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -385,7 +385,7 @@
/**
* @hide
*/
- public static final int CACHE_USER_DATA_SIZE = 4;
+ public static final int CACHE_USER_DATA_SIZE = 32;
private static final class AccountKeyData {
final public Account account;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c8e1e4d..0947e33 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3701,7 +3701,7 @@
* @see View#findViewById(int)
* @see Activity#requireViewById(int)
*/
- @Nullable
+ /* TODO(b/347672184): Re-add @Nullable */
public <T extends View> T findViewById(@IdRes int id) {
return getWindow().findViewById(id);
}
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index f2debfc..4bfa3b3 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -82,7 +82,7 @@
private void reportChange(long changeId, int state, boolean isLoggable) {
int uid = Process.myUid();
- mChangeReporter.reportChange(uid, changeId, state, isLoggable);
+ mChangeReporter.reportChange(uid, changeId, state, false, isLoggable);
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5956e2b..80764af 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1128,7 +1128,7 @@
private static final PropertyInvalidatedCache<Integer, GetPackagesForUidResult>
mGetPackagesForUidCache =
new PropertyInvalidatedCache<Integer, GetPackagesForUidResult>(
- 32, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) {
+ 1024, CACHE_KEY_PACKAGES_FOR_UID_PROPERTY) {
@Override
public GetPackagesForUidResult recompute(Integer uid) {
try {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 1925380..62b5412 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -397,6 +397,23 @@
}
/**
+ * Sets the component name of the
+ * {@link android.service.notification.ConditionProviderService} that manages this rule
+ * (but note that {@link android.service.notification.ConditionProviderService} is
+ * deprecated in favor of using {@link NotificationManager#setAutomaticZenRuleState} to
+ * notify the system about the state of your rule).
+ *
+ * <p>This is exclusive with {@link #setConfigurationActivity}; rules where a configuration
+ * activity is set will not use the component set here to determine whether the rule
+ * should be active.
+ *
+ * @hide
+ */
+ public void setOwner(@Nullable ComponentName owner) {
+ this.owner = owner;
+ }
+
+ /**
* Sets the configuration activity - an activity that handles
* {@link NotificationManager#ACTION_AUTOMATIC_ZEN_RULE} that shows the user more information
* about this rule and/or allows them to configure it. This is required to be non-null for rules
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3b9a5d3..baed4fd 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2303,19 +2303,26 @@
&& PermissionManager.DEVICE_AWARE_PERMISSIONS.contains(permission)) {
VirtualDeviceManager virtualDeviceManager =
getSystemService(VirtualDeviceManager.class);
- VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
- if (virtualDevice != null) {
- if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
- && !virtualDevice.hasCustomAudioInputSupport())
- || (Objects.equals(permission, Manifest.permission.CAMERA)
- && !virtualDevice.hasCustomCameraSupport())) {
- deviceId = Context.DEVICE_ID_DEFAULT;
- }
- } else {
+ if (virtualDeviceManager == null) {
Slog.e(
TAG,
- "virtualDevice is not found when device id is not default. deviceId = "
+ "VDM is not enabled when device id is not default. deviceId = "
+ deviceId);
+ } else {
+ VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+ if (virtualDevice != null) {
+ if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
+ && !virtualDevice.hasCustomAudioInputSupport())
+ || (Objects.equals(permission, Manifest.permission.CAMERA)
+ && !virtualDevice.hasCustomCameraSupport())) {
+ deviceId = Context.DEVICE_ID_DEFAULT;
+ }
+ } else {
+ Slog.e(
+ TAG,
+ "virtualDevice is not found when device id is not default. deviceId = "
+ + deviceId);
+ }
}
}
@@ -3169,6 +3176,11 @@
public void updateDeviceId(int updatedDeviceId) {
if (updatedDeviceId != Context.DEVICE_ID_DEFAULT) {
VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
+ if (vdm == null) {
+ throw new IllegalArgumentException(
+ "VDM is not enabled when updating to non-default device id: "
+ + updatedDeviceId);
+ }
if (!vdm.isValidVirtualDeviceId(updatedDeviceId)) {
throw new IllegalArgumentException(
"Not a valid ID of the default device or any virtual device: "
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index cd7f1e4..57d9a71 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -565,7 +565,7 @@
* @see View#findViewById(int)
* @see Dialog#requireViewById(int)
*/
- @Nullable
+ /* TODO(b/347672184): Re-add @Nullable */
public <T extends View> T findViewById(@IdRes int id) {
return mWindow.findViewById(id);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a1c4267..aea15e1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5058,6 +5058,7 @@
* @see Notification#fullScreenIntent
*/
@NonNull
+ @RequiresPermission(android.Manifest.permission.USE_FULL_SCREEN_INTENT)
public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
mN.fullScreenIntent = intent;
setFlag(FLAG_HIGH_PRIORITY, highPriority);
@@ -6050,6 +6051,7 @@
bindProfileBadge(contentView, p);
bindAlertedIcon(contentView, p);
bindExpandButton(contentView, p);
+ bindCloseButton(contentView, p);
mN.mUsesStandardHeader = true;
}
@@ -6071,6 +6073,15 @@
contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
}
+ private void bindCloseButton(RemoteViews contentView, StandardTemplateParams p) {
+ // set default colors
+ int bgColor = getBackgroundColor(p);
+ int backgroundColor = Colors.flattenAlpha(getColors(p).getProtectionColor(), bgColor);
+ int foregroundColor = Colors.flattenAlpha(getPrimaryTextColor(p), backgroundColor);
+ contentView.setInt(R.id.close_button, "setForegroundColor", foregroundColor);
+ contentView.setInt(R.id.close_button, "setBackgroundColor", backgroundColor);
+ }
+
private void bindHeaderChronometerAndTime(RemoteViews contentView,
StandardTemplateParams p, boolean hasTextToLeft) {
if (!p.mHideTime && showsTimeOrChronometer()) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 3f6c81b..326d7ce 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -72,6 +72,35 @@
public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
/**
+ * A reserved id for a system channel reserved for promotional notifications.
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final String PROMOTIONS_ID = "android.app.promotions";
+ /**
+ * A reserved id for a system channel reserved for non-conversation social media notifications.
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final String SOCIAL_MEDIA_ID = "android.app.social";
+ /**
+ * A reserved id for a system channel reserved for news notifications.
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final String NEWS_ID = "android.app.news";
+ /**
+ * A reserved id for a system channel reserved for content recommendation notifications.
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final String RECS_ID = "android.app.recs";
+
+ /**
* The formatter used by the system to create an id for notification
* channels when it automatically creates conversation channels on behalf of an app. The format
* string takes two arguments, in this order: the
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 370aff8..1050e1d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -30,6 +30,7 @@
import android.content.res.CompatResources;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.Flags;
import android.content.res.Resources;
import android.content.res.ResourcesImpl;
import android.content.res.ResourcesKey;
@@ -138,16 +139,22 @@
private final ArrayMap<String, SharedLibraryAssets> mSharedLibAssetsMap =
new ArrayMap<>();
+ @VisibleForTesting
+ public ArrayMap<String, SharedLibraryAssets> getRegisteredResourcePaths() {
+ return mSharedLibAssetsMap;
+ }
+
/**
* The internal function to register the resources paths of a package (e.g. a shared library).
* This will collect the package resources' paths from its ApplicationInfo and add them to all
* existing and future contexts while the application is running.
*/
public void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) {
- SharedLibraryAssets sharedLibAssets = new SharedLibraryAssets(appInfo.sourceDir,
- appInfo.splitSourceDirs, appInfo.sharedLibraryFiles,
- appInfo.resourceDirs, appInfo.overlayPaths);
+ if (!Flags.registerResourcePaths()) {
+ return;
+ }
+ final var sharedLibAssets = new SharedLibraryAssets(appInfo);
synchronized (mLock) {
if (mSharedLibAssetsMap.containsKey(uniqueId)) {
Slog.v(TAG, "Package resources' paths for uniqueId: " + uniqueId
@@ -155,18 +162,37 @@
return;
}
mSharedLibAssetsMap.put(uniqueId, sharedLibAssets);
- appendLibAssetsLocked(sharedLibAssets.getAllAssetPaths());
- Slog.v(TAG, "The following resources' paths have been added: "
- + Arrays.toString(sharedLibAssets.getAllAssetPaths()));
+ appendLibAssetsLocked(sharedLibAssets);
+ Slog.v(TAG, "The following library key has been added: "
+ + sharedLibAssets.getResourcesKey());
}
}
- private static class ApkKey {
+ /**
+ * Apply the registered library paths to the passed impl object
+ * @return the hash code for the current version of the registered paths
+ */
+ public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) {
+ if (!Flags.registerResourcePaths()) {
+ return 0;
+ }
+
+ final var collector = new PathCollector(null);
+ final int size = mSharedLibAssetsMap.size();
+ for (int i = 0; i < size; i++) {
+ final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
+ collector.appendKey(libraryKey);
+ }
+ impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey()));
+ return size;
+ }
+
+ public static class ApkKey {
public final String path;
public final boolean sharedLib;
public final boolean overlay;
- ApkKey(String path, boolean sharedLib, boolean overlay) {
+ public ApkKey(String path, boolean sharedLib, boolean overlay) {
this.path = path;
this.sharedLib = sharedLib;
this.overlay = overlay;
@@ -190,6 +216,12 @@
return this.path.equals(other.path) && this.sharedLib == other.sharedLib
&& this.overlay == other.overlay;
}
+
+ @Override
+ public String toString() {
+ return "ApkKey[" + (sharedLib ? "lib" : "app") + (overlay ? ", overlay" : "") + ": "
+ + path + "]";
+ }
}
/**
@@ -505,7 +537,10 @@
return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
}
- private @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
+ /**
+ * Loads the ApkAssets object for the passed key, or picks the one from the cache if available.
+ */
+ public @NonNull ApkAssets loadApkAssets(@NonNull final ApkKey key) throws IOException {
ApkAssets apkAssets;
// Optimistically check if this ApkAssets exists somewhere else.
@@ -747,8 +782,8 @@
private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
@NonNull ResourcesKey key, @Nullable ApkAssetsSupplier apkSupplier) {
ResourcesImpl impl = findResourcesImplForKeyLocked(key);
- // ResourcesImpl also need to be recreated if its shared library count is not up-to-date.
- if (impl == null || impl.getSharedLibCount() != mSharedLibAssetsMap.size()) {
+ // ResourcesImpl also need to be recreated if its shared library hash is not up-to-date.
+ if (impl == null || impl.getAppliedSharedLibsHash() != mSharedLibAssetsMap.size()) {
impl = createResourcesImpl(key, apkSupplier);
if (impl != null) {
mResourceImpls.put(key, new WeakReference<>(impl));
@@ -1533,54 +1568,107 @@
}
}
- private void appendLibAssetsLocked(String[] libAssets) {
- synchronized (mLock) {
- // Record which ResourcesImpl need updating
- // (and what ResourcesKey they should update to).
- final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ /**
+ * A utility class to collect resources paths into a ResourcesKey object:
+ * - Separates the libraries and the overlays into different sets as those are loaded in
+ * different ways.
+ * - Allows to start with an existing original key object, and copies all non-path related
+ * properties into the final one.
+ * - Preserves the path order while dropping all duplicates in an efficient manner.
+ */
+ private static class PathCollector {
+ public final ResourcesKey originalKey;
+ public final ArrayList<String> orderedLibs = new ArrayList<>();
+ public final ArraySet<String> libsSet = new ArraySet<>();
+ public final ArrayList<String> orderedOverlays = new ArrayList<>();
+ public final ArraySet<String> overlaysSet = new ArraySet<>();
- final int implCount = mResourceImpls.size();
- for (int i = 0; i < implCount; i++) {
- final ResourcesKey key = mResourceImpls.keyAt(i);
- final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
- final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl == null) {
- Slog.w(TAG, "Found a ResourcesImpl which is null, skip it and continue to "
- + "append shared library assets for next ResourcesImpl.");
- continue;
- }
+ static void appendNewPath(@NonNull String path,
+ @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) {
+ if (uniquePaths.add(path)) {
+ orderedPaths.add(path);
+ }
+ }
- var newDirs = new ArrayList<String>();
- var dirsSet = new ArraySet<String>();
- if (key.mLibDirs != null) {
- final int dirsLength = key.mLibDirs.length;
- for (int k = 0; k < dirsLength; k++) {
- newDirs.add(key.mLibDirs[k]);
- dirsSet.add(key.mLibDirs[k]);
- }
- }
- final int assetsLength = libAssets.length;
- for (int j = 0; j < assetsLength; j++) {
- if (dirsSet.add(libAssets[j])) {
- newDirs.add(libAssets[j]);
- }
- }
- String[] newLibAssets = newDirs.toArray(new String[0]);
- if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
- updatedResourceKeys.put(impl, new ResourcesKey(
- key.mResDir,
- key.mSplitResDirs,
- key.mOverlayPaths,
- newLibAssets,
- key.mDisplayId,
- key.mOverrideConfiguration,
- key.mCompatInfo,
- key.mLoaders));
- }
+ static void appendAllNewPaths(@Nullable String[] paths,
+ @NonNull ArraySet<String> uniquePaths, @NonNull ArrayList<String> orderedPaths) {
+ if (paths == null) return;
+ for (int i = 0, size = paths.length; i < size; i++) {
+ appendNewPath(paths[i], uniquePaths, orderedPaths);
+ }
+ }
+
+ PathCollector(@Nullable ResourcesKey original) {
+ originalKey = original;
+ if (originalKey != null) {
+ appendKey(originalKey);
+ }
+ }
+
+ public void appendKey(@NonNull ResourcesKey key) {
+ appendAllNewPaths(key.mLibDirs, libsSet, orderedLibs);
+ appendAllNewPaths(key.mOverlayPaths, overlaysSet, orderedOverlays);
+ }
+
+ boolean isSameAsOriginal() {
+ if (originalKey == null) {
+ return orderedLibs.isEmpty() && orderedOverlays.isEmpty();
+ }
+ return ((originalKey.mLibDirs == null && orderedLibs.isEmpty())
+ || (originalKey.mLibDirs != null
+ && originalKey.mLibDirs.length == orderedLibs.size()))
+ && ((originalKey.mOverlayPaths == null && orderedOverlays.isEmpty())
+ || (originalKey.mOverlayPaths != null
+ && originalKey.mOverlayPaths.length == orderedOverlays.size()));
+ }
+
+ @NonNull ResourcesKey collectedKey() {
+ return new ResourcesKey(
+ originalKey == null ? null : originalKey.mResDir,
+ originalKey == null ? null : originalKey.mSplitResDirs,
+ orderedOverlays.toArray(new String[0]), orderedLibs.toArray(new String[0]),
+ originalKey == null ? 0 : originalKey.mDisplayId,
+ originalKey == null ? null : originalKey.mOverrideConfiguration,
+ originalKey == null ? null : originalKey.mCompatInfo,
+ originalKey == null ? null : originalKey.mLoaders);
+ }
+ }
+
+ /**
+ * Takes the original resources key and the one containing a set of library paths and overlays
+ * to append, and combines them together. In case when the original key already contains all
+ * those paths this function returns null, otherwise it makes a new ResourcesKey object.
+ */
+ private @Nullable ResourcesKey createNewResourceKeyIfNeeded(
+ @NonNull ResourcesKey original, @NonNull ResourcesKey library) {
+ final var collector = new PathCollector(original);
+ collector.appendKey(library);
+ return collector.isSameAsOriginal() ? null : collector.collectedKey();
+ }
+
+ /**
+ * Append the newly registered shared library asset paths to all existing resources objects.
+ */
+ private void appendLibAssetsLocked(@NonNull SharedLibraryAssets libAssets) {
+ // Record the ResourcesImpl's that need updating, and what ResourcesKey they should
+ // update to.
+ final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+ final int implCount = mResourceImpls.size();
+ for (int i = 0; i < implCount; i++) {
+ final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ if (impl == null) {
+ Slog.w(TAG, "Found a null ResourcesImpl, skipped.");
+ continue;
}
- redirectAllResourcesToNewImplLocked(updatedResourceKeys);
+ final var newKey = createNewResourceKeyIfNeeded(key, libAssets.getResourcesKey());
+ if (newKey != null) {
+ updatedResourceKeys.put(impl, newKey);
+ }
}
+ redirectAllResourcesToNewImplLocked(updatedResourceKeys);
}
private void applyNewResourceDirsLocked(@Nullable final String[] oldSourceDirs,
@@ -1718,8 +1806,9 @@
}
}
- // Another redirect function which will loop through all Resources and reload ResourcesImpl
- // if it needs a shared library asset paths update.
+ // Another redirect function which will loop through all Resources in the process, even the ones
+ // the app created outside of the regular Android Runtime, and reload their ResourcesImpl if it
+ // needs a shared library asset paths update.
private void redirectAllResourcesToNewImplLocked(
@NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) {
cleanupReferences(mAllResourceReferences, mAllResourceReferencesQueue);
@@ -1745,10 +1834,15 @@
if (r.getImpl() != null) {
final ResourcesImpl oldImpl = r.getImpl();
// ResourcesImpl constructor will help to append shared library asset paths.
- final ResourcesImpl newImpl = new ResourcesImpl(oldImpl.getAssets(),
- oldImpl.getMetrics(), oldImpl.getConfiguration(),
- oldImpl.getDisplayAdjustments());
- r.setImpl(newImpl);
+ if (oldImpl.getAssets().isUpToDate()) {
+ final ResourcesImpl newImpl = new ResourcesImpl(oldImpl.getAssets(),
+ oldImpl.getMetrics(), oldImpl.getConfiguration(),
+ oldImpl.getDisplayAdjustments());
+ r.setImpl(newImpl);
+ } else {
+ Slog.w(TAG, "Skip appending shared library asset paths for the "
+ + "Resource as its assets are not up to date.");
+ }
}
}
}
@@ -1830,52 +1924,35 @@
}
}
- public static class SharedLibraryAssets{
- private final String[] mAssetPaths;
+ @VisibleForTesting
+ public static class SharedLibraryAssets {
+ private final ResourcesKey mResourcesKey;
- SharedLibraryAssets(String sourceDir, String[] splitSourceDirs, String[] sharedLibraryFiles,
- String[] resourceDirs, String[] overlayPaths) {
- mAssetPaths = collectAssetPaths(sourceDir, splitSourceDirs, sharedLibraryFiles,
- resourceDirs, overlayPaths);
- }
-
- private @NonNull String[] collectAssetPaths(String sourceDir, String[] splitSourceDirs,
- String[] sharedLibraryFiles, String[] resourceDirs, String[] overlayPaths) {
- final String[][] inputLists = {
- splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths
- };
-
- final ArraySet<String> assetPathSet = new ArraySet<>();
- final List<String> assetPathList = new ArrayList<>();
- if (sourceDir != null) {
- assetPathSet.add(sourceDir);
- assetPathList.add(sourceDir);
- }
-
- for (int i = 0; i < inputLists.length; i++) {
- if (inputLists[i] != null) {
- for (int j = 0; j < inputLists[i].length; j++) {
- if (assetPathSet.add(inputLists[i][j])) {
- assetPathList.add(inputLists[i][j]);
- }
- }
- }
- }
- return assetPathList.toArray(new String[0]);
+ private SharedLibraryAssets(ApplicationInfo appInfo) {
+ // We're loading all library's files as shared libs, regardless where they are in
+ // its own ApplicationInfo.
+ final var collector = new PathCollector(null);
+ PathCollector.appendNewPath(appInfo.sourceDir, collector.libsSet,
+ collector.orderedLibs);
+ PathCollector.appendAllNewPaths(appInfo.splitSourceDirs, collector.libsSet,
+ collector.orderedLibs);
+ PathCollector.appendAllNewPaths(appInfo.sharedLibraryFiles, collector.libsSet,
+ collector.orderedLibs);
+ PathCollector.appendAllNewPaths(appInfo.resourceDirs, collector.overlaysSet,
+ collector.orderedOverlays);
+ PathCollector.appendAllNewPaths(appInfo.overlayPaths, collector.overlaysSet,
+ collector.orderedOverlays);
+ mResourcesKey = collector.collectedKey();
}
/**
- * @return all the asset paths of this collected in this class.
+ * @return the resources key for this library assets.
*/
- public @NonNull String[] getAllAssetPaths() {
- return mAssetPaths;
+ public @NonNull ResourcesKey getResourcesKey() {
+ return mResourcesKey;
}
}
- public @NonNull ArrayMap<String, SharedLibraryAssets> getSharedLibAssetsMap() {
- return new ArrayMap<>(mSharedLibAssetsMap);
- }
-
/**
* Add all resources references to the list which is designed to help to append shared library
* asset paths. This is invoked in Resources constructor to include all Resources instances.
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 348d4d8f..a249c39 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1647,10 +1647,13 @@
// Calling out without a lock held.
mUiAutomationConnection.executeShellCommand(command, sink, null);
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error executing shell command!", ioe);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error executing shell command!", re);
+ } catch (IOException | RemoteException e) {
+ Log.e(LOG_TAG, "Error executing shell command!", e);
+ } catch (IllegalArgumentException | NullPointerException | SecurityException e) {
+ // An exception of these types is propagated from the server.
+ // Rethrow it to keep the old behavior. To avoid FD leak, close the source.
+ IoUtils.closeQuietly(source);
+ throw e;
} finally {
IoUtils.closeQuietly(sink);
}
@@ -1734,10 +1737,15 @@
// Calling out without a lock held.
mUiAutomationConnection.executeShellCommandWithStderr(
command, sink_read, source_write, stderr_sink_read);
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error executing shell command!", ioe);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error executing shell command!", re);
+ } catch (IOException | RemoteException e) {
+ Log.e(LOG_TAG, "Error executing shell command!", e);
+ } catch (IllegalArgumentException | SecurityException | NullPointerException e) {
+ // An exception of these types is propagated from the server.
+ // Rethrow it to keep the old behavior. To avoid FD leaks, close the sources.
+ IoUtils.closeQuietly(sink_write);
+ IoUtils.closeQuietly(source_read);
+ IoUtils.closeQuietly(stderr_source_read);
+ throw e;
} finally {
IoUtils.closeQuietly(sink_read);
IoUtils.closeQuietly(source_write);
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 3c4bd9e..5e21e05 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -550,8 +550,21 @@
try {
process = Runtime.getRuntime().exec(command);
- } catch (IOException exc) {
- throw new RuntimeException("Error running shell command '" + command + "'", exc);
+ } catch (IOException ex) {
+ // Make sure the passed FDs are closed.
+ IoUtils.closeQuietly(sink);
+ IoUtils.closeQuietly(source);
+ IoUtils.closeQuietly(stderrSink);
+ // No to need to wrap in RuntimeException. Only to keep the old behavior.
+ // This is just logged and not propagated to the remote caller anyway.
+ throw new RuntimeException("Error running shell command '" + command + "'", ex);
+ } catch (IllegalArgumentException | NullPointerException | SecurityException ex) {
+ // Make sure the passed FDs are closed.
+ IoUtils.closeQuietly(sink);
+ IoUtils.closeQuietly(source);
+ IoUtils.closeQuietly(stderrSink);
+ // Rethrow the exception. This will be propagated to the remote caller.
+ throw ex;
}
// Read from process and write to pipe
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
index dea4e9c8..7948cec 100644
--- a/core/java/android/app/compat/ChangeIdStateCache.java
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -32,7 +32,7 @@
public final class ChangeIdStateCache
extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
private static final String CACHE_KEY = "cache_key.is_compat_change_enabled";
- private static final int MAX_ENTRIES = 64;
+ private static final int MAX_ENTRIES = 2048;
private static boolean sDisabled = false;
private volatile IPlatformCompat mPlatformCompat;
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 9a04ded..6fb852f 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -52,8 +52,8 @@
* List of transaction items that should be executed in order. Including both
* {@link ActivityLifecycleItem} and other {@link ClientTransactionItem}.
*/
- @Nullable
- private List<ClientTransactionItem> mTransactionItems;
+ @NonNull
+ private final List<ClientTransactionItem> mTransactionItems = new ArrayList<>();
/** @deprecated use {@link #getTransactionItems} instead. */
@Nullable
@@ -89,12 +89,7 @@
* @param item A single message that can contain a client activity/window request/callback.
*/
public void addTransactionItem(@NonNull ClientTransactionItem item) {
- if (Flags.bundleClientTransactionFlag()) {
- if (mTransactionItems == null) {
- mTransactionItems = new ArrayList<>();
- }
- mTransactionItems.add(item);
- }
+ mTransactionItems.add(item);
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
// Populate even if mTransactionItems is set to support the UnsupportedAppUsage.
@@ -107,9 +102,8 @@
/**
* Gets the list of client window requests/callbacks.
- * TODO(b/260873529): must be non null after remove the deprecated methods.
*/
- @Nullable
+ @NonNull
public List<ClientTransactionItem> getTransactionItems() {
return mTransactionItems;
}
@@ -197,22 +191,9 @@
* requested by transaction items.
*/
public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) {
- if (mTransactionItems != null) {
- final int size = mTransactionItems.size();
- for (int i = 0; i < size; ++i) {
- mTransactionItems.get(i).preExecute(clientTransactionHandler);
- }
- return;
- }
-
- if (mActivityCallbacks != null) {
- final int size = mActivityCallbacks.size();
- for (int i = 0; i < size; ++i) {
- mActivityCallbacks.get(i).preExecute(clientTransactionHandler);
- }
- }
- if (mLifecycleStateRequest != null) {
- mLifecycleStateRequest.preExecute(clientTransactionHandler);
+ final int size = mTransactionItems.size();
+ for (int i = 0; i < size; ++i) {
+ mTransactionItems.get(i).preExecute(clientTransactionHandler);
}
}
@@ -252,29 +233,13 @@
if (Flags.disableObjectPool()) {
return;
}
- if (mTransactionItems != null) {
- int size = mTransactionItems.size();
- for (int i = 0; i < size; i++) {
- mTransactionItems.get(i).recycle();
- }
- mTransactionItems = null;
- mActivityCallbacks = null;
- mLifecycleStateRequest = null;
- } else {
- // Only needed when mTransactionItems is null, otherwise these will have the same
- // reference as mTransactionItems to support UnsupportedAppUsage.
- if (mActivityCallbacks != null) {
- int size = mActivityCallbacks.size();
- for (int i = 0; i < size; i++) {
- mActivityCallbacks.get(i).recycle();
- }
- mActivityCallbacks = null;
- }
- if (mLifecycleStateRequest != null) {
- mLifecycleStateRequest.recycle();
- mLifecycleStateRequest = null;
- }
+ int size = mTransactionItems.size();
+ for (int i = 0; i < size; i++) {
+ mTransactionItems.get(i).recycle();
}
+ mTransactionItems.clear();
+ mActivityCallbacks = null;
+ mLifecycleStateRequest = null;
mClient = null;
mActivityToken = null;
ObjectPool.recycle(this);
@@ -286,60 +251,24 @@
@SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final.
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- final boolean writeTransactionItems = mTransactionItems != null;
- dest.writeBoolean(writeTransactionItems);
- if (writeTransactionItems) {
- dest.writeParcelableList(mTransactionItems, flags);
- } else {
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- // Only write mLifecycleStateRequest and mActivityCallbacks when mTransactionItems is
- // null
- dest.writeParcelable(mLifecycleStateRequest, flags);
- final boolean writeActivityCallbacks = mActivityCallbacks != null;
- dest.writeBoolean(writeActivityCallbacks);
- if (writeActivityCallbacks) {
- dest.writeParcelableList(mActivityCallbacks, flags);
- }
- }
+ dest.writeParcelableList(mTransactionItems, flags);
}
/** Read from Parcel. */
private ClientTransaction(@NonNull Parcel in) {
- final boolean readTransactionItems = in.readBoolean();
- if (readTransactionItems) {
- mTransactionItems = new ArrayList<>();
- in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
- ClientTransactionItem.class);
+ in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
+ ClientTransactionItem.class);
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- // Populate mLifecycleStateRequest and mActivityCallbacks from mTransactionItems so
- // that they have the same reference when there is UnsupportedAppUsage to those fields.
- final int size = mTransactionItems.size();
- for (int i = 0; i < size; i++) {
- final ClientTransactionItem item = mTransactionItems.get(i);
- if (item.isActivityLifecycleItem()) {
- setLifecycleStateRequest((ActivityLifecycleItem) item);
- } else {
- addCallback(item);
- }
- }
- } else {
- // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
- // Only read mLifecycleStateRequest and mActivityCallbacks when mTransactionItems is
- // null
- mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(),
- ActivityLifecycleItem.class);
- setActivityTokenIfNotSet(mLifecycleStateRequest);
- final boolean readActivityCallbacks = in.readBoolean();
- if (readActivityCallbacks) {
- mActivityCallbacks = new ArrayList<>();
- in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(),
- ClientTransactionItem.class);
- final int size = mActivityCallbacks.size();
- for (int i = 0; mActivityToken == null && i < size; i++) {
- final ClientTransactionItem item = mActivityCallbacks.get(i);
- setActivityTokenIfNotSet(item);
- }
+ // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+ // Populate mLifecycleStateRequest and mActivityCallbacks from mTransactionItems so
+ // that they have the same reference when there is UnsupportedAppUsage to those fields.
+ final int size = mTransactionItems.size();
+ for (int i = 0; i < size; i++) {
+ final ClientTransactionItem item = mTransactionItems.get(i);
+ if (item.isActivityLifecycleItem()) {
+ setLifecycleStateRequest((ActivityLifecycleItem) item);
+ } else {
+ addCallback(item);
}
}
}
@@ -390,25 +319,12 @@
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("ClientTransaction{");
- if (mTransactionItems != null) {
- // #addTransactionItem
- sb.append("\n transactionItems=[");
- final int size = mTransactionItems.size();
- for (int i = 0; i < size; i++) {
- sb.append("\n ").append(mTransactionItems.get(i));
- }
- sb.append("\n ]");
- } else {
- // #addCallback
- sb.append("\n callbacks=[");
- final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
- for (int i = 0; i < size; i++) {
- sb.append("\n ").append(mActivityCallbacks.get(i));
- }
- sb.append("\n ]");
- // #setLifecycleStateRequest
- sb.append("\n stateRequest=").append(mLifecycleStateRequest);
+ sb.append("\n transactionItems=[");
+ final int size = mTransactionItems.size();
+ for (int i = 0; i < size; i++) {
+ sb.append("\n ").append(mTransactionItems.get(i));
}
+ sb.append("\n ]");
sb.append("\n}");
return sb.toString();
}
@@ -417,41 +333,18 @@
void dump(@NonNull String prefix, @NonNull PrintWriter pw,
@NonNull ClientTransactionHandler transactionHandler) {
pw.append(prefix).println("ClientTransaction{");
- if (mTransactionItems != null) {
- pw.append(prefix).print(" transactionItems=[");
- final String itemPrefix = prefix + " ";
- final int size = mTransactionItems.size();
- if (size > 0) {
- pw.println();
- for (int i = 0; i < size; i++) {
- mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler);
- }
- pw.append(prefix).println(" ]");
- } else {
- pw.println("]");
- }
- pw.append(prefix).println("}");
- return;
- }
- pw.append(prefix).print(" callbacks=[");
+ pw.append(prefix).print(" transactionItems=[");
final String itemPrefix = prefix + " ";
- final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
+ final int size = mTransactionItems.size();
if (size > 0) {
pw.println();
for (int i = 0; i < size; i++) {
- mActivityCallbacks.get(i).dump(itemPrefix, pw, transactionHandler);
+ mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler);
}
pw.append(prefix).println(" ]");
} else {
pw.println("]");
}
-
- pw.append(prefix).println(" stateRequest=");
- if (mLifecycleStateRequest != null) {
- mLifecycleStateRequest.dump(itemPrefix, pw, transactionHandler);
- } else {
- pw.append(itemPrefix).println("null");
- }
pw.append(prefix).println("}");
}
}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 9b53461..0c1e7a3 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -20,7 +20,6 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.window.flags.Flags.activityWindowInfoFlag;
-import static com.android.window.flags.Flags.bundleClientTransactionFlag;
import static java.util.Objects.requireNonNull;
@@ -196,7 +195,7 @@
/** Called before updating the Configuration of the given {@code context}. */
public void onContextConfigurationPreChanged(@NonNull Context context) {
- if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
+ if (ActivityThread.isSystem()) {
// Not enable for system server.
return;
}
@@ -212,7 +211,7 @@
/** Called after updating the Configuration of the given {@code context}. */
public void onContextConfigurationPostChanged(@NonNull Context context) {
- if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
+ if (ActivityThread.isSystem()) {
// Not enable for system server.
return;
}
diff --git a/core/java/android/app/servertransaction/RefreshCallbackItem.java b/core/java/android/app/servertransaction/RefreshCallbackItem.java
index 368ed76..a3f82e9 100644
--- a/core/java/android/app/servertransaction/RefreshCallbackItem.java
+++ b/core/java/android/app/servertransaction/RefreshCallbackItem.java
@@ -29,8 +29,8 @@
/**
* Callback that allows to {@link TransactionExecutor#cycleToPath} to {@link ON_PAUSE} or
- * {@link ON_STOP} in {@link TransactionExecutor#executeCallbacks} for activity "refresh" flow
- * that goes through "paused -> resumed" or "stopped -> resumed" cycle.
+ * {@link ON_STOP} in {@link TransactionExecutor#executeTransactionItems} for activity "refresh"
+ * flow that goes through "paused -> resumed" or "stopped -> resumed" cycle.
*
* <p>This is used in combination with {@link com.android.server.wm.DisplayRotationCompatPolicy}
* for camera compatibility treatment that handles orientation mismatch between camera buffers and
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 480205e..68012e2 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -26,7 +26,6 @@
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName;
import static android.app.servertransaction.TransactionExecutorHelper.getStateName;
-import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
import static android.app.servertransaction.TransactionExecutorHelper.shouldExcludeLastLifecycleState;
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
@@ -77,13 +76,7 @@
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionExecuted");
try {
- if (transaction.getTransactionItems() != null) {
- executeTransactionItems(transaction);
- } else {
- // TODO(b/260873529): cleanup after launch.
- executeCallbacks(transaction);
- executeLifecycleState(transaction);
- }
+ executeTransactionItems(transaction);
} catch (Exception e) {
Slog.e(TAG, "Failed to execute the transaction: "
+ transactionToString(transaction, mTransactionHandler));
@@ -112,41 +105,6 @@
}
}
- /**
- * Cycle through all states requested by callbacks and execute them at proper times.
- * @deprecated use {@link #executeTransactionItems} instead.
- */
- @VisibleForTesting
- @Deprecated
- public void executeCallbacks(@NonNull ClientTransaction transaction) {
- final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
- if (callbacks == null || callbacks.isEmpty()) {
- // No callbacks to execute, return early.
- return;
- }
- if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");
-
- // In case when post-execution state of the last callback matches the final state requested
- // for the activity in this transaction, we won't do the last transition here and do it when
- // moving to final state instead (because it may contain additional parameters from server).
- final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
- final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
- : UNDEFINED;
- // Index of the last callback that requests some post-execution state.
- final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
-
- final int size = callbacks.size();
- for (int i = 0; i < size; ++i) {
- final ClientTransactionItem item = callbacks.get(i);
-
- // Skip the very last transition and perform it by explicit state request instead.
- final int postExecutionState = item.getPostExecutionState();
- final boolean shouldExcludeLastLifecycleState = postExecutionState != UNDEFINED
- && i == lastCallbackRequestingState && finalState == postExecutionState;
- executeNonLifecycleItem(transaction, item, shouldExcludeLastLifecycleState);
- }
- }
-
private void executeNonLifecycleItem(@NonNull ClientTransaction transaction,
@NonNull ClientTransactionItem item, boolean shouldExcludeLastLifecycleState) {
final IBinder token = item.getActivityToken();
@@ -184,21 +142,6 @@
}
}
- /**
- * Transition to the final state if requested by the transaction.
- * @deprecated use {@link #executeTransactionItems} instead
- */
- @Deprecated
- private void executeLifecycleState(@NonNull ClientTransaction transaction) {
- final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
- if (lifecycleItem == null) {
- // No lifecycle request, return early.
- return;
- }
-
- executeLifecycleItem(transaction, lifecycleItem);
- }
-
private void executeLifecycleItem(@NonNull ClientTransaction transaction,
@NonNull ActivityLifecycleItem lifecycleItem) {
final IBinder token = lifecycleItem.getActivityToken();
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 710261a..9f622e9 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -226,29 +226,6 @@
}
/**
- * Return the index of the last callback that requests the state in which activity will be after
- * execution. If there is a group of callbacks in the end that requests the same specific state
- * or doesn't request any - we will find the first one from such group.
- *
- * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any
- * specific state. If there is a sequence
- * Configuration - ActivityResult - Configuration - ActivityResult
- * index 1 will be returned, because ActivityResult request on position 1 will be the last
- * request that moves activity to the RESUMED state where it will eventually end.
- * @deprecated to be removed with {@link TransactionExecutor#executeCallbacks}.
- */
- @Deprecated
- static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) {
- final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
- if (callbacks == null || callbacks.isEmpty()
- || transaction.getLifecycleStateRequest() == null) {
- return -1;
- }
- return lastCallbackRequestingStateIndex(callbacks, 0, callbacks.size() - 1,
- transaction.getLifecycleStateRequest().getActivityToken());
- }
-
- /**
* Returns the index of the last callback between the start index and last index that requests
* the state for the given activity token in which that activity will be after execution.
* If there is a group of callbacks in the end that requests the same specific state or doesn't
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3213b40..abb562d 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -523,6 +523,8 @@
private final IAppWidgetService mService;
private final DisplayMetrics mDisplayMetrics;
+ private int mMaxBitmapMemory = 0;
+
private boolean mHasPostedLegacyLists = false;
/**
@@ -548,6 +550,12 @@
if (mService == null) {
return;
}
+ // Allowing some buffer when estimating the maximum bitmap cache size
+ try {
+ mMaxBitmapMemory = (int) (mService.getMaxBitmapMemory() * 0.9);
+ } catch (Exception e) {
+ Log.e(TAG, "Error setting the maximum bitmap memory", e);
+ }
BackgroundThread.getExecutor().execute(() -> {
try {
mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName,
@@ -576,7 +584,7 @@
final RemoteViews viewsCopy = new RemoteViews(original);
Runnable updateWidgetWithTask = () -> {
try {
- viewsCopy.collectAllIntents().get();
+ viewsCopy.collectAllIntents(mMaxBitmapMemory).get();
action.acceptOrThrow(viewsCopy);
} catch (Exception e) {
Log.e(TAG, failureMsg, e);
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 18cfca6..4e0379e 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -50,3 +50,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "remote_views_proto"
+ namespace: "app_widgets"
+ description: "Enable support for persisting RemoteViews previews to Protobuf"
+ bug: "306546610"
+}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index b4ad1c8..34cfa58 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -21,6 +21,8 @@
import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER;
import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH;
+import static java.util.Collections.unmodifiableMap;
+
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -56,6 +58,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
+import android.util.ArrayMap;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -75,6 +78,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
@@ -119,29 +123,31 @@
* is created successfully.
*/
public static final int RESULT_OK = -1;
-
+ //TODO(b/331459560) Need to update the java doc after API cut for W.
/**
* The result code to propagate back to the user activity, indicates if the association dialog
* is implicitly cancelled.
* E.g. phone is locked, switch to another app or press outside the dialog.
*/
public static final int RESULT_CANCELED = 0;
-
+ //TODO(b/331459560) Need to update the java doc after API cut for W.
/**
* The result code to propagate back to the user activity, indicates the association dialog
* is explicitly declined by the users.
*/
public static final int RESULT_USER_REJECTED = 1;
-
+ //TODO(b/331459560) Need to update the java doc after API cut for W.
/**
* The result code to propagate back to the user activity, indicates the association
* dialog is dismissed if there's no device found after 20 seconds.
*/
public static final int RESULT_DISCOVERY_TIMEOUT = 2;
-
+ //TODO(b/331459560) Need to update the java doc after API cut for W.
/**
* The result code to propagate back to the user activity, indicates the internal error
* in CompanionDeviceManager.
+ * E.g. Missing necessary permissions or duplicate {@link AssociationRequest}s when create the
+ * {@link AssociationInfo}.
*/
public static final int RESULT_INTERNAL_ERROR = 3;
@@ -368,12 +374,22 @@
*/
public void onAssociationCreated(@NonNull AssociationInfo associationInfo) {}
+ //TODO(b/331459560): Add deprecated and remove abstract after API cut for W.
/**
* Invoked if the association could not be created.
*
* @param error error message.
*/
public abstract void onFailure(@Nullable CharSequence error);
+
+ /**
+ * Invoked if the association could not be created.
+ *
+ * @param resultCode indicate the particular reason why the association
+ * could not be created.
+ */
+ @FlaggedApi(Flags.FLAG_ASSOCIATION_FAILURE_CODE)
+ public void onFailure(@ResultCode int resultCode) {}
}
private final ICompanionDeviceManager mService;
@@ -1803,8 +1819,12 @@
}
@Override
- public void onFailure(CharSequence error) throws RemoteException {
- execute(mCallback::onFailure, error);
+ public void onFailure(@ResultCode int resultCode) {
+ if (Flags.associationFailureCode()) {
+ execute(mCallback::onFailure, resultCode);
+ }
+
+ execute(mCallback::onFailure, RESULT_CODE_TO_REASON.get(resultCode));
}
private <T> void execute(Consumer<T> callback, T arg) {
@@ -1988,4 +2008,15 @@
}
}
}
+
+ private static final Map<Integer, String> RESULT_CODE_TO_REASON;
+ static {
+ final Map<Integer, String> map = new ArrayMap<>();
+ map.put(RESULT_CANCELED, REASON_CANCELED);
+ map.put(RESULT_USER_REJECTED, REASON_USER_REJECTED);
+ map.put(RESULT_DISCOVERY_TIMEOUT, REASON_DISCOVERY_TIMEOUT);
+ map.put(RESULT_INTERNAL_ERROR, REASON_INTERNAL_ERROR);
+
+ RESULT_CODE_TO_REASON = unmodifiableMap(map);
+ }
}
diff --git a/core/java/android/companion/IAssociationRequestCallback.aidl b/core/java/android/companion/IAssociationRequestCallback.aidl
index 8cc2a71..b1be30a 100644
--- a/core/java/android/companion/IAssociationRequestCallback.aidl
+++ b/core/java/android/companion/IAssociationRequestCallback.aidl
@@ -25,5 +25,5 @@
oneway void onAssociationCreated(in AssociationInfo associationInfo);
- oneway void onFailure(in CharSequence error);
+ oneway void onFailure(in int resultCode);
}
\ No newline at end of file
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index fd4ba83..ee9114f 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -53,4 +53,12 @@
namespace: "companion"
description: "Unpair with an associated bluetooth device"
bug: "322237619"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "association_failure_code"
+ is_exported: true
+ namespace: "companion"
+ description: "Enable association failure code API"
+ bug: "331459560"
+}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 24f18cc..0653839 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -17,7 +17,9 @@
package android.companion.virtual;
import android.app.PendingIntent;
+import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
+import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.VirtualSensor;
@@ -35,6 +37,8 @@
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualRotaryEncoderConfig;
+import android.hardware.input.VirtualRotaryEncoderScrollEvent;
import android.hardware.input.VirtualStylusButtonEvent;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualStylusMotionEvent;
@@ -158,6 +162,12 @@
void createVirtualStylus(in VirtualStylusConfig config, IBinder token);
/**
+ * Creates a new rotary encoder and registers it with the input framework with the given token.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void createVirtualRotaryEncoder(in VirtualRotaryEncoderConfig config, IBinder token);
+
+ /**
* Removes the input device corresponding to the given token from the framework.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
@@ -218,6 +228,12 @@
boolean sendStylusButtonEvent(IBinder token, in VirtualStylusButtonEvent event);
/**
+ * Injects a scroll event from the virtual rotary encoder corresponding to the given token.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ boolean sendRotaryEncoderScrollEvent(IBinder token, in VirtualRotaryEncoderScrollEvent event);
+
+ /**
* Returns all virtual sensors created for this device.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
@@ -282,4 +298,15 @@
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
String getVirtualCameraId(in VirtualCameraConfig camera);
+
+ /**
+ * Setter for listeners that live in the client process, namely in
+ * {@link android.companion.virtual.VirtualDeviceInternal}.
+ *
+ * This is needed for virtual devices that are created by the system, as the VirtualDeviceImpl
+ * object is created before the returned VirtualDeviceInternal one.
+ */
+ @EnforcePermission("CREATE_VIRTUAL_DEVICE")
+ void setListeners(in IVirtualDeviceActivityListener activityListener,
+ in IVirtualDeviceSoundEffectListener soundEffectListener);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 60448ba..4cbcb68 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -46,6 +46,8 @@
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualNavigationTouchpad;
import android.hardware.input.VirtualNavigationTouchpadConfig;
+import android.hardware.input.VirtualRotaryEncoder;
+import android.hardware.input.VirtualRotaryEncoderConfig;
import android.hardware.input.VirtualStylus;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualTouchscreen;
@@ -162,6 +164,20 @@
mSoundEffectListener);
}
+ VirtualDeviceInternal(
+ IVirtualDeviceManager service,
+ Context context,
+ IVirtualDevice virtualDevice) {
+ mService = service;
+ mContext = context.getApplicationContext();
+ mVirtualDevice = virtualDevice;
+ try {
+ mVirtualDevice.setListeners(mActivityListenerBinder, mSoundEffectListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
int getDeviceId() {
try {
return mVirtualDevice.getDeviceId();
@@ -335,6 +351,19 @@
}
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ VirtualRotaryEncoder createVirtualRotaryEncoder(@NonNull VirtualRotaryEncoderConfig config) {
+ try {
+ final IBinder token = new Binder(
+ "android.hardware.input.VirtualRotaryEncoder:" + config.getInputDeviceName());
+ mVirtualDevice.createVirtualRotaryEncoder(config, token);
+ return new VirtualRotaryEncoder(config, mVirtualDevice, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@NonNull
VirtualNavigationTouchpad createVirtualNavigationTouchpad(
@NonNull VirtualNavigationTouchpadConfig config) {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index ed55a3f..88c3d38 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -56,6 +56,8 @@
import android.hardware.input.VirtualMouseConfig;
import android.hardware.input.VirtualNavigationTouchpad;
import android.hardware.input.VirtualNavigationTouchpadConfig;
+import android.hardware.input.VirtualRotaryEncoder;
+import android.hardware.input.VirtualRotaryEncoderConfig;
import android.hardware.input.VirtualStylus;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualTouchscreen;
@@ -573,6 +575,12 @@
new VirtualDeviceInternal(service, context, associationId, params);
}
+ /** @hide */
+ public VirtualDevice(IVirtualDeviceManager service, Context context,
+ IVirtualDevice virtualDevice) {
+ mVirtualDeviceInternal = new VirtualDeviceInternal(service, context, virtualDevice);
+ }
+
/**
* Returns the unique ID of this virtual device.
*/
@@ -947,6 +955,23 @@
}
/**
+ * Creates a virtual rotary encoder.
+ *
+ * @param config the configuration for the virtual rotary encoder.
+ * @see android.view.InputDevice#SOURCE_ROTARY_ENCODER
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_VIRTUAL_ROTARY)
+ public VirtualRotaryEncoder createVirtualRotaryEncoder(
+ @NonNull VirtualRotaryEncoderConfig config) {
+ if (!android.companion.virtualdevice.flags.Flags.virtualRotary()) {
+ throw new UnsupportedOperationException("Virtual rotary support not enabled");
+ }
+ return mVirtualDeviceInternal.createVirtualRotaryEncoder(config);
+ }
+
+ /**
* Creates a VirtualAudioDevice, capable of recording audio emanating from this device,
* or injecting audio from another device.
*
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 1e781532..cd8082c 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -47,6 +47,14 @@
}
flag {
+ name: "virtual_rotary"
+ is_exported: true
+ namespace: "virtual_devices"
+ description: "Enable virtual rotary input"
+ bug: "320328752"
+}
+
+flag {
namespace: "virtual_devices"
name: "device_aware_drm"
description: "Makes MediaDrm APIs device-aware"
diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java
index fb9536f..3acb373 100644
--- a/core/java/android/content/ComponentCallbacks.java
+++ b/core/java/android/content/ComponentCallbacks.java
@@ -58,7 +58,9 @@
* @deprecated Since API level 14 this is superseded by
* {@link ComponentCallbacks2#onTrimMemory}.
* Since API level 34 this is never called.
- * Apps targeting API level 34 and above may provide an empty implementation.
+ * If you're overriding ComponentCallbacks2#onTrimMemory and
+ * your minSdkVersion is greater than API 14, you can provide
+ * an empty implementation for this method.
*/
@Deprecated
void onLowMemory();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4fcf6b6..24fd000 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4451,7 +4451,8 @@
* @see #DISPLAY_HASH_SERVICE
* @see android.view.displayhash.DisplayHashManager
*/
- public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
+ // TODO(b/347269120): Re-add @Nullable
+ public abstract Object getSystemService(@ServiceName @NonNull String name);
/**
* Return the handle to a system-level service by class.
@@ -4495,7 +4496,8 @@
* <b>never</b> throw a {@link RuntimeException} if the name is not supported.
*/
@SuppressWarnings("unchecked")
- public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) {
+ // TODO(b/347269120): Re-add @Nullable
+ public final <T> T getSystemService(@NonNull Class<T> serviceClass) {
// Because subclasses may override getSystemService(String) we cannot
// perform a lookup by class alone. We must first map the class to its
// service name then invoke the string-based method.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e0cf0a5..a475c29 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -932,7 +932,8 @@
}
@Override
- public @Nullable Object getSystemService(String name) {
+ // TODO(b/347269120): Re-add @Nullable
+ public Object getSystemService(String name) {
return mBase.getSystemService(name);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index dfa29738..112507e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6068,7 +6068,6 @@
* @see #CHOOSER_CONTENT_TYPE_ALBUM
* @see #createChooser(Intent, CharSequence)
*/
- @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_ALBUM_TEXT)
public static final String EXTRA_CHOOSER_CONTENT_TYPE_HINT =
"android.intent.extra.CHOOSER_CONTENT_TYPE_HINT";
@@ -6085,7 +6084,6 @@
*
* @see #EXTRA_CHOOSER_CONTENT_TYPE_HINT
*/
- @FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_ALBUM_TEXT)
public static final int CHOOSER_CONTENT_TYPE_ALBUM = 1;
/**
@@ -7630,6 +7628,13 @@
| FLAG_GRANT_PREFIX_URI_PERMISSION;
/**
+ * Flags that are not normally set by application code, but set for you by the system.
+ */
+ private static final int SYSTEM_ONLY_FLAGS = FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | FLAG_ACTIVITY_BROUGHT_TO_FRONT
+ | FLAG_RECEIVER_FROM_SHELL;
+
+ /**
* Local flag indicating this instance was created by copy constructor.
*/
private static final int LOCAL_FLAG_FROM_COPY = 1 << 0;
@@ -7682,6 +7687,11 @@
@TestApi
public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0;
+ /**
+ * Extended flags that are not normally set by application code, but set for you by the system.
+ */
+ private static final int SYSTEM_ONLY_EXTENDED_FLAGS = EXTENDED_FLAG_FILTER_MISMATCH;
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// toUri() and parseUri() options.
@@ -12625,6 +12635,28 @@
}
}
+ /**
+ * Prepare this {@link Intent} to enter system_server.
+ *
+ * @hide
+ */
+ public void prepareToEnterSystemServer() {
+ // Refuse possible leaked file descriptors
+ if (hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // These flags are set only by the system, and should be stripped out as soon as the intent
+ // is received by system_server from the caller so it can be properly updated later.
+ removeFlags(SYSTEM_ONLY_FLAGS);
+ removeExtendedFlags(SYSTEM_ONLY_EXTENDED_FLAGS);
+ if (mOriginalIntent != null) {
+ mOriginalIntent.prepareToEnterSystemServer();
+ }
+ if (mSelector != null) {
+ mSelector.prepareToEnterSystemServer();
+ }
+ }
+
/** @hide */
public boolean hasWebURI() {
if (getData() == null) {
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index a37408b..6d9dc45 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -14,3 +14,4 @@
per-file ComponentCallbacksController = charlesccchen@google.com
per-file AttributionSource* = file:/core/java/android/permission/OWNERS
per-file Broadcast* = file:/BROADCASTS_OWNERS
+per-file ComponentCallbacks*.java = file:/PERFORMANCE_OWNERS
\ No newline at end of file
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4f06209..57ffed4 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1331,6 +1331,7 @@
* This change id is the gatekeeper for all treatments that force a given min aspect ratio.
* Enabling this change will allow the following min aspect ratio treatments to be applied:
* <ul>
+ * <li>OVERRIDE_MIN_ASPECT_RATIO_SMALL
* <li>OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
* <li>OVERRIDE_MIN_ASPECT_RATIO_LARGE
* </ul>
@@ -1372,6 +1373,22 @@
public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
/**
+ * This change id sets the activity's min aspect ratio to a small value as defined by
+ * OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ // TODO(b/349060719): Add CTS tests.
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_SMALL = 349045028L; // buganizer id
+
+ /** @hide Small override aspect ratio, currently 4:3. */
+ public static final float OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE = 4 / 3f;
+
+ /**
* This change id sets the activity's min aspect ratio to a medium value as defined by
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 282ede3..8c56a9d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -11436,7 +11436,7 @@
private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>
sApplicationInfoCache =
new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>(
- 32, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getApplicationInfo") {
@Override
public ApplicationInfo recompute(ApplicationInfoQuery query) {
@@ -11537,7 +11537,7 @@
private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
sPackageInfoCache =
new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
- 64, PermissionManager.CACHE_KEY_PACKAGE_INFO,
+ 2048, PermissionManager.CACHE_KEY_PACKAGE_INFO,
"getPackageInfo") {
@Override
public PackageInfo recompute(PackageInfoQuery query) {
diff --git a/core/java/android/content/pm/dex/OWNERS b/core/java/android/content/pm/dex/OWNERS
index 267e5d58..558b5f7 100644
--- a/core/java/android/content/pm/dex/OWNERS
+++ b/core/java/android/content/pm/dex/OWNERS
@@ -1,7 +1,6 @@
# Bug component: 86431
-toddke@android.com
-toddke@google.com
patb@google.com
-calin@google.com
ngeoffray@google.com
+jiakaiz@google.com
+mast@google.com
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index a9c07d1..6f5bb4a 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -126,7 +126,7 @@
description: "Talkback focus doesn't move to the 'If you change your Google Account picture…' after swiping next to move the focus from 'Choose a picture'"
bug: "330835921"
metadata {
- purpose: PURPOSE_BUGFIX
+ purpose: PURPOSE_BUGFIX
}
}
@@ -136,7 +136,7 @@
description: "Talkback doesn't announce 'selected' after double tapping the button in the picture list in 'Choose a picture' page."
bug: "330840549"
metadata {
- purpose: PURPOSE_BUGFIX
+ purpose: PURPOSE_BUGFIX
}
}
@@ -146,10 +146,21 @@
description: "Fix potential unexpected behavior due to concurrent file writing"
bug: "339351031"
metadata {
- purpose: PURPOSE_BUGFIX
+ purpose: PURPOSE_BUGFIX
}
}
+flag {
+ name: "cache_user_serial_number"
+ namespace: "multiuser"
+ description: "Optimise user serial number retrieval"
+ bug: "340018451"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
# This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
flag {
name: "enable_private_space_features"
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 273e40a..899c2d6 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -17,6 +17,7 @@
package android.content.res;
import static android.content.res.Resources.ID_NULL;
+import static android.app.ResourcesManager.ApkKey;
import android.annotation.AnyRes;
import android.annotation.ArrayRes;
@@ -26,12 +27,14 @@
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.TestApi;
+import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
import android.content.res.loader.ResourcesLoader;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -264,7 +267,7 @@
}
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
- sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
+ sSystemApkAssets = apkAssets.toArray(new ApkAssets[0]);
if (sSystem == null) {
sSystem = new AssetManager(true /*sentinel*/);
}
@@ -448,7 +451,7 @@
@Deprecated
@UnsupportedAppUsage
public int addAssetPath(String path) {
- return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
+ return addAssetPathInternal(List.of(new ApkKey(path, false, false)), false);
}
/**
@@ -458,7 +461,7 @@
@Deprecated
@UnsupportedAppUsage
public int addAssetPathAsSharedLibrary(String path) {
- return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
+ return addAssetPathInternal(List.of(new ApkKey(path, true, false)), false);
}
/**
@@ -468,55 +471,109 @@
@Deprecated
@UnsupportedAppUsage
public int addOverlayPath(String path) {
- return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
+ return addAssetPathInternal(List.of(new ApkKey(path, false, true)), false);
}
/**
* @hide
*/
- public void addSharedLibraryPaths(@NonNull String[] paths) {
- final int length = paths.length;
- for (int i = 0; i < length; i++) {
- addAssetPathInternal(paths[i], false, true);
+ public void addPresetApkKeys(@NonNull List<ApkKey> keys) {
+ addAssetPathInternal(keys, true);
+ }
+
+ private int addAssetPathInternal(List<ApkKey> apkKeys, boolean presetAssets) {
+ Objects.requireNonNull(apkKeys, "apkKeys");
+ if (apkKeys.isEmpty()) {
+ return 0;
+ }
+
+ synchronized (this) {
+ ensureOpenLocked();
+
+ // See if we already have some of the apkKeys loaded.
+ final int originalAssetsCount = mApkAssets.length;
+
+ // Getting an assets' path is a relatively expensive operation, cache them.
+ final ArrayMap<String, Integer> assetPaths = new ArrayMap<>(originalAssetsCount);
+ for (int i = 0; i < originalAssetsCount; i++) {
+ assetPaths.put(mApkAssets[i].getAssetPath(), i);
+ }
+
+ final var newKeys = new ArrayList<ApkKey>(apkKeys.size());
+ int lastFoundIndex = -1;
+ for (int i = 0, pathsSize = apkKeys.size(); i < pathsSize; i++) {
+ final var key = apkKeys.get(i);
+ final var index = assetPaths.get(key.path);
+ if (index == null) {
+ newKeys.add(key);
+ } else {
+ lastFoundIndex = index;
+ }
+ }
+ if (newKeys.isEmpty()) {
+ return lastFoundIndex + 1;
+ }
+
+ final var newAssets = loadAssets(newKeys);
+ if (newAssets.isEmpty()) {
+ return 0;
+ }
+ mApkAssets = makeNewAssetsArrayLocked(newAssets);
+ nativeSetApkAssets(mObject, mApkAssets, true, presetAssets);
+ invalidateCachesLocked(-1);
+ return originalAssetsCount + 1;
}
}
- private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
- Objects.requireNonNull(path, "path");
- synchronized (this) {
- ensureOpenLocked();
- final int count = mApkAssets.length;
-
- // See if we already have it loaded.
- for (int i = 0; i < count; i++) {
- if (mApkAssets[i].getAssetPath().equals(path)) {
- return i + 1;
- }
+ /**
+ * Insert the new assets preserving the correct order: all non-loader assets go before all
+ * of the loader assets.
+ */
+ @GuardedBy("this")
+ private @NonNull ApkAssets[] makeNewAssetsArrayLocked(
+ @NonNull ArrayList<ApkAssets> newNonLoaderAssets) {
+ final int originalAssetsCount = mApkAssets.length;
+ int firstLoaderIndex = originalAssetsCount;
+ for (int i = 0; i < originalAssetsCount; i++) {
+ if (mApkAssets[i].isForLoader()) {
+ firstLoaderIndex = i;
+ break;
}
-
- final ApkAssets assets;
- try {
- if (overlay) {
- // TODO(b/70343104): This hardcoded path will be removed once
- // addAssetPathInternal is deleted.
- final String idmapPath = "/data/resource-cache/"
- + path.substring(1).replace('/', '@')
- + "@idmap";
- assets = ApkAssets.loadOverlayFromPath(idmapPath, 0 /* flags */);
- } else {
- assets = ApkAssets.loadFromPath(path,
- appAsLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
- }
- } catch (IOException e) {
- return 0;
- }
-
- mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
- mApkAssets[count] = assets;
- nativeSetApkAssets(mObject, mApkAssets, true, false);
- invalidateCachesLocked(-1);
- return count + 1;
}
+ final int newAssetsSize = newNonLoaderAssets.size();
+ final var newAssetsArray = new ApkAssets[originalAssetsCount + newAssetsSize];
+ if (firstLoaderIndex > 0) {
+ // This should always be true, but who knows...
+ System.arraycopy(mApkAssets, 0, newAssetsArray, 0, firstLoaderIndex);
+ }
+ for (int i = 0; i < newAssetsSize; i++) {
+ newAssetsArray[firstLoaderIndex + i] = newNonLoaderAssets.get(i);
+ }
+ if (originalAssetsCount > firstLoaderIndex) {
+ System.arraycopy(
+ mApkAssets, firstLoaderIndex,
+ newAssetsArray, firstLoaderIndex + newAssetsSize,
+ originalAssetsCount - firstLoaderIndex);
+ }
+ return newAssetsArray;
+ }
+
+ private static @NonNull ArrayList<ApkAssets> loadAssets(@NonNull ArrayList<ApkKey> keys) {
+ final int pathsSize = keys.size();
+ final var loadedAssets = new ArrayList<ApkAssets>(pathsSize);
+ final var resourcesManager = ResourcesManager.getInstance();
+ for (int i = 0; i < pathsSize; i++) {
+ final var key = keys.get(i);
+ try {
+ // ResourcesManager has a cache of loaded assets, ensuring we don't open the same
+ // file repeatedly, which is useful for the common overlays and registered
+ // shared libraries.
+ loadedAssets.add(resourcesManager.loadApkAssets(key));
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to load asset, key = " + key, e);
+ }
+ }
+ return loadedAssets;
}
/** @hide */
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 5031faa..7b18117 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -32,6 +32,9 @@
import android.util.SparseArray;
import android.util.StateSet;
import android.util.Xml;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
import com.android.internal.R;
import com.android.internal.graphics.ColorUtils;
@@ -44,7 +47,9 @@
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
*
@@ -793,4 +798,61 @@
return new ColorStateList(stateSpecs, colors);
}
};
+
+ /** @hide */
+ public void writeToProto(ProtoOutputStream out) {
+ for (int[] states : mStateSpecs) {
+ long specToken = out.start(ColorStateListProto.STATE_SPECS);
+ for (int state : states) {
+ out.write(ColorStateListProto.StateSpec.STATE, state);
+ }
+ out.end(specToken);
+ }
+ for (int color : mColors) {
+ out.write(ColorStateListProto.COLORS, color);
+ }
+ }
+
+ /** @hide */
+ public static ColorStateList createFromProto(ProtoInputStream in)
+ throws Exception {
+ List<int[]> stateSpecs = new ArrayList<>();
+ List<Integer> colors = new ArrayList<>();
+
+
+ while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) ColorStateListProto.COLORS:
+ colors.add(in.readInt(ColorStateListProto.COLORS));
+ break;
+ case (int) ColorStateListProto.STATE_SPECS:
+ final long stateToken = in.start(ColorStateListProto.STATE_SPECS);
+ List<Integer> states = new ArrayList<>();
+ while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) ColorStateListProto.StateSpec.STATE:
+ states.add(in.readInt(ColorStateListProto.StateSpec.STATE));
+ break;
+ default:
+ Log.w(TAG, "Unhandled field while reading Icon proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ int[] statesArray = new int[states.size()];
+ Arrays.setAll(statesArray, states::get);
+ stateSpecs.add(statesArray);
+ in.end(stateToken);
+ break;
+ default:
+ Log.w(TAG, "Unhandled field while reading Icon proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+
+ int[][] stateSpecsArray = new int[stateSpecs.size()][];
+ Arrays.setAll(stateSpecsArray, stateSpecs::get);
+ int[] colorsArray = new int[colors.size()];
+ Arrays.setAll(colorsArray, colors::get);
+ return new ColorStateList(stateSpecsArray, colorsArray);
+ }
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 31cacb7..d874270 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -29,7 +29,6 @@
import android.annotation.StyleableRes;
import android.app.LocaleConfig;
import android.app.ResourcesManager;
-import android.app.ResourcesManager.SharedLibraryAssets;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
@@ -48,7 +47,6 @@
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.Trace;
-import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -147,10 +145,9 @@
// Cyclical cache used for recently-accessed XML files.
private int mLastCachedXmlBlockIndex = -1;
- // The number of shared libraries registered within this ResourcesImpl, which is designed to
- // help to determine whether this ResourcesImpl is outdated on shared library information and
- // needs to be replaced.
- private int mSharedLibCount;
+ // The hash that allows to detect when the shared libraries applied to this object have changed,
+ // and it is outdated and needs to be replaced.
+ private final int mAppliedSharedLibsHash;
private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
@@ -204,15 +201,8 @@
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
mAssets = assets;
- if (Flags.registerResourcePaths()) {
- ArrayMap<String, SharedLibraryAssets> sharedLibMap =
- ResourcesManager.getInstance().getSharedLibAssetsMap();
- final int size = sharedLibMap.size();
- for (int i = 0; i < size; i++) {
- assets.addSharedLibraryPaths(sharedLibMap.valueAt(i).getAllAssetPaths());
- }
- mSharedLibCount = sharedLibMap.size();
- }
+ mAppliedSharedLibsHash =
+ ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this);
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
@@ -1615,7 +1605,7 @@
}
}
- public int getSharedLibCount() {
- return mSharedLibCount;
+ public int getAppliedSharedLibsHash() {
+ return mAppliedSharedLibsHash;
}
}
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index b98a0d8..c521b96 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -232,7 +232,17 @@
oemComponentName,
PackageManager.ComponentInfoFlags.of(
PackageManager.MATCH_SYSTEM_ONLY));
- if (info.enabled && info.exported) {
+ boolean oemComponentEnabled = info.enabled;
+ int runtimeComponentEnabledState = context.getPackageManager()
+ .getComponentEnabledSetting(oemComponentName);
+ if (runtimeComponentEnabledState == PackageManager
+ .COMPONENT_ENABLED_STATE_ENABLED) {
+ oemComponentEnabled = true;
+ } else if (runtimeComponentEnabledState == PackageManager
+ .COMPONENT_ENABLED_STATE_DISABLED) {
+ oemComponentEnabled = false;
+ }
+ if (oemComponentEnabled && info.exported) {
intentResultBuilder.setOemUiUsageStatus(IntentCreationResult
.OemUiUsageStatus.SUCCESS);
Slog.i(TAG,
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index faa2c70..e0dc568 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -239,8 +239,8 @@
NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
} catch (SQLiteCantOpenDatabaseException e) {
- final StringBuilder message = new StringBuilder("Cannot open database '")
- .append(file).append('\'')
+ final StringBuilder message = new StringBuilder(e.getMessage())
+ .append(" '").append(file).append("'")
.append(" with flags 0x")
.append(Integer.toHexString(mConfiguration.openFlags));
@@ -265,12 +265,10 @@
message.append(": File ").append(path).append(" is not readable");
} else if (Files.isDirectory(path)) {
message.append(": Path ").append(path).append(" is a directory");
- } else {
- message.append(": Unable to deduct failure reason");
}
} catch (Throwable th) {
- message.append(": Unable to deduct failure reason"
- + " because filesystem couldn't be examined: ").append(th.getMessage());
+ // Ignore any exceptions generated whilst attempting to create extended diagnostic
+ // messages.
}
throw new SQLiteCantOpenDatabaseException(message.toString(), e);
} finally {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 39b7f85..cbac912 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -365,7 +365,9 @@
VirtualDeviceManager virtualDeviceManager =
context.getSystemService(VirtualDeviceManager.class);
- return virtualDeviceManager.getDevicePolicy(context.getDeviceId(), POLICY_TYPE_CAMERA);
+ return virtualDeviceManager == null
+ ? DEVICE_POLICY_DEFAULT
+ : virtualDeviceManager.getDevicePolicy(context.getDeviceId(), POLICY_TYPE_CAMERA);
}
/**
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index d2a2f12..51ad151 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -5,7 +5,7 @@
sumir@google.com
# Camera
-per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhenwang@google.com,yinchiayeh@google.com,zhijunhe@google.com,jchowdhary@google.com
+per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhenwang@google.com,zhijunhe@google.com,jchowdhary@google.com
# Sensor Privacy
per-file *SensorPrivacy* = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index fdd8b04..678bd6b 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -137,6 +137,7 @@
BIOMETRIC_WEAK,
BIOMETRIC_CONVENIENCE,
DEVICE_CREDENTIAL,
+ MANDATORY_BIOMETRICS,
})
@Retention(RetentionPolicy.SOURCE)
@interface Types {}
@@ -214,6 +215,21 @@
*/
int DEVICE_CREDENTIAL = 1 << 15;
+ /**
+ * The bit is used to request for mandatory biometrics.
+ *
+ * <p> The requirements to trigger mandatory biometrics are as follows:
+ * 1. User must have enabled the toggle for mandatory biometrics is settings
+ * 2. User must have enrollments for all {@link #BIOMETRIC_STRONG} sensors available
+ * 3. The device must not be in a trusted location
+ * </p>
+ *
+ * <p> If all the above conditions are satisfied, only {@link #BIOMETRIC_STRONG} sensors
+ * will be eligible for authentication, and device credential fallback will be dropped.
+ * @hide
+ */
+ int MANDATORY_BIOMETRICS = 1 << 16;
+
}
/**
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 37a2df8..42f5fc8 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -794,10 +794,15 @@
public void onDialogDismissed(int reason) {
// Check the reason and invoke OnClickListener(s) if necessary
if (reason == DISMISSED_REASON_NEGATIVE) {
- mNegativeButtonInfo.executor.execute(() -> {
- mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
- mIsPromptShowing = false;
- });
+ if (mNegativeButtonInfo != null) {
+ mNegativeButtonInfo.executor.execute(() -> {
+ mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+ mIsPromptShowing = false;
+ });
+ } else {
+ mAuthenticationCallback.onAuthenticationError(BIOMETRIC_ERROR_USER_CANCELED,
+ null /* errString */);
+ }
} else if (reason == DISMISSED_REASON_CONTENT_VIEW_MORE_OPTIONS) {
if (mContentViewMoreOptionsButtonInfo != null) {
mContentViewMoreOptionsButtonInfo.executor.execute(() -> {
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index ba9f30d..901f6b7 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -195,6 +195,10 @@
return true;
} else if (mContentView != null && isContentViewMoreOptionsButtonUsed()) {
return true;
+ } else if (Flags.mandatoryBiometrics()
+ && (mAuthenticators & BiometricManager.Authenticators.MANDATORY_BIOMETRICS)
+ != 0) {
+ return true;
}
return false;
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 6fffb82..056ca93 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -564,7 +564,7 @@
* {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, then this list will only contain
* CONTROL_ZOOM_RATIO_RANGE and SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
*
- * @see INFO_SESSION_CONFIGURATION_QUERY_VERSION
+ * @see #INFO_SESSION_CONFIGURATION_QUERY_VERSION
*/
@NonNull
@FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 2b86891..2dbd4b8 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -586,7 +586,9 @@
if (mVirtualDeviceManager == null) {
mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class);
}
- return mVirtualDeviceManager.getDevicePolicy(context.getDeviceId(), POLICY_TYPE_CAMERA);
+ return mVirtualDeviceManager == null
+ ? DEVICE_POLICY_DEFAULT
+ : mVirtualDeviceManager.getDevicePolicy(context.getDeviceId(), POLICY_TYPE_CAMERA);
}
// TODO(b/147726300): Investigate how to support foldables/multi-display devices.
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6968f27..fbed50a 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -57,7 +57,7 @@
*
* <p>CaptureRequests can be created by using a {@link Builder} instance,
* obtained by calling {@link CameraDevice#createCaptureRequest} or {@link
- * CameraManager#createCaptureRequest}</p>
+ * CameraDevice.CameraDeviceSetup#createCaptureRequest}</p>
*
* <p>CaptureRequests are given to {@link CameraCaptureSession#capture} or
* {@link CameraCaptureSession#setRepeatingRequest} to capture images from a camera.</p>
@@ -84,7 +84,7 @@
* @see CameraCaptureSession#setRepeatingBurst
* @see CameraDevice#createCaptureRequest
* @see CameraDevice#createReprocessCaptureRequest
- * @see CameraManager#createCaptureRequest
+ * @see CameraDevice.CameraDeviceSetup#createCaptureRequest
*/
public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
implements Parcelable {
@@ -812,8 +812,9 @@
* A builder for capture requests.
*
* <p>To obtain a builder instance, use the
- * {@link CameraDevice#createCaptureRequest} or {@link CameraManager#createCaptureRequest}
- * method, which initializes the request fields to one of the templates defined in
+ * {@link CameraDevice#createCaptureRequest} or
+ * {@link CameraDevice.CameraDeviceSetup#createCaptureRequest} method, which
+ * initializes the request fields to one of the templates defined in
* {@link CameraDevice}.
*
* @see CameraDevice#createCaptureRequest
@@ -822,7 +823,7 @@
* @see CameraDevice#TEMPLATE_STILL_CAPTURE
* @see CameraDevice#TEMPLATE_VIDEO_SNAPSHOT
* @see CameraDevice#TEMPLATE_MANUAL
- * @see CameraManager#createCaptureRequest
+ * @see CameraDevice.CameraDeviceSetup#createCaptureRequest
*/
public final static class Builder {
diff --git a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
index 69a6e9b..bf3f59f 100644
--- a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
@@ -23,12 +23,13 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraExtensionCharacteristics.Extension;
import android.hardware.camera2.CameraExtensionSession;
+import android.media.ImageReader;
+
+import com.android.internal.camera.flags.Flags;
import java.util.List;
import java.util.concurrent.Executor;
-import com.android.internal.camera.flags.Flags;
-
/**
* A class that aggregates all supported arguments for
* {@link CameraExtensionSession} initialization.
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index f3b7b91..fb2c2f0 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -260,7 +260,7 @@
* smaller sizes, then the resulting
* {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
* be tested either by calling {@link CameraDevice#createCaptureSession} or
- * {@link CameraDeviceSetup#isSessionConfigurationSupported}.
+ * {@link CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}.
*
* @return non-modifiable ascending list of available sizes.
*/
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 3b2913c..0c55ed5 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -130,8 +130,8 @@
* Create a new {@link SessionConfiguration} with sessionType and output configurations.
*
* <p>The SessionConfiguration objects created by this constructor can be used by
- * {@link CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} to query a camera device's feature
+ * {@link CameraDeviceSetup#isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup#getSessionCharacteristics} to query a camera device's feature
* combination support and session specific characteristics. For the SessionConfiguration
* object to be used to create a capture session, {@link #setStateCallback} must be called to
* specify the state callback function, and any incomplete OutputConfigurations must be
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index b2dcf90..91caedc 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -740,6 +740,12 @@
*/
void onBlockingScreenOn(Runnable unblocker);
+ /**
+ * Called while display is turning to screen state other than state ON to notify that any
+ * pending work from the previous blockScreenOn call should have been cancelled.
+ */
+ void cancelBlockScreenOn();
+
/** Whether auto brightness update in doze is allowed */
boolean allowAutoBrightnessInDoze();
}
@@ -774,6 +780,12 @@
boolean blockScreenOn(Runnable unblocker);
/**
+ * Called while display is turning to screen state other than state ON to notify that any
+ * pending work from the previous blockScreenOn call should have been cancelled.
+ */
+ void cancelBlockScreenOn();
+
+ /**
* Get the brightness levels used to determine automatic brightness based on lux levels.
* @param mode The auto-brightness mode
* (AutomaticBrightnessController.AutomaticBrightnessMode)
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 53771e3..21c9002 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1025,8 +1025,18 @@
/**
* Monitor input on the specified display for gestures.
*
+ * NOTE: New usages of Gesture Monitors are strongly discouraged. Gesture Monitors are
+ * deprecated, in favor of spy windows (see {@link LayoutParams#INPUT_FEATURE_SPY}).
+ * The spy window should be configured specifically to receive the desired events,
+ * unlike the gesture monitor which receives all events on the display.
+ *
* @hide
+ * @deprecated
+ * @see LayoutParams#INPUT_FEATURE_SPY
+ * @see android.os.InputConfig#SPY
+ * @see #pilferPointers(IBinder)
*/
+ @Deprecated
public InputMonitor monitorGestureInput(String name, int displayId) {
return mGlobal.monitorGestureInput(name, displayId);
}
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoder.java b/core/java/android/hardware/input/VirtualRotaryEncoder.java
new file mode 100644
index 0000000..bc131df
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.companion.virtualdevice.flags.Flags;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A virtual rotary encoder representing a scroll input mechanism on a remote device.
+ *
+ * <p>This registers an {@link android.view.InputDevice} that is interpreted like a
+ * physically-connected device and dispatches received events to it.</p>
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_ROTARY)
+@SystemApi
+public class VirtualRotaryEncoder extends VirtualInputDevice {
+ /** @hide */
+ public VirtualRotaryEncoder(VirtualRotaryEncoderConfig config, IVirtualDevice virtualDevice,
+ IBinder token) {
+ super(config, virtualDevice, token);
+ }
+
+ /**
+ * Sends a scroll event to the system.
+ *
+ * @param event the event to send
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendScrollEvent(@NonNull VirtualRotaryEncoderScrollEvent event) {
+ try {
+ if (!mVirtualDevice.sendRotaryEncoderScrollEvent(mToken, event)) {
+ Log.w(TAG, "Failed to send scroll event from virtual rotary "
+ + mConfig.getInputDeviceName());
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.aidl
similarity index 87%
rename from services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
rename to core/java/android/hardware/input/VirtualRotaryEncoderConfig.aidl
index bb8db8a..2d9a899 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package com.android.server.dreams;
+package android.hardware.input;
-public class TestDreamSettingsActivity {
-}
+parcelable VirtualRotaryEncoderConfig;
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoderConfig.java b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.java
new file mode 100644
index 0000000..8d81fa9
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderConfig.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Configuration for creating a virtual rotary encoder.
+ *
+ * @see android.companion.virtual.VirtualDeviceManager.VirtualDevice#createVirtualRotaryEncoder
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_ROTARY)
+@SystemApi
+public final class VirtualRotaryEncoderConfig extends VirtualInputDeviceConfig
+ implements Parcelable {
+ @NonNull
+ public static final Creator<VirtualRotaryEncoderConfig> CREATOR = new Creator<>() {
+ @Override
+ public VirtualRotaryEncoderConfig createFromParcel(Parcel in) {
+ return new VirtualRotaryEncoderConfig(in);
+ }
+
+ @Override
+ public VirtualRotaryEncoderConfig[] newArray(int size) {
+ return new VirtualRotaryEncoderConfig[size];
+ }
+ };
+
+ private VirtualRotaryEncoderConfig(@NonNull VirtualRotaryEncoderConfig.Builder builder) {
+ super(builder);
+ }
+
+ private VirtualRotaryEncoderConfig(@NonNull Parcel in) {
+ super(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Builder for creating a {@link VirtualRotaryEncoderConfig}.
+ */
+ public static final class Builder extends VirtualInputDeviceConfig.Builder<Builder> {
+
+ /**
+ * Builds the {@link VirtualRotaryEncoderConfig} instance.
+ */
+ @NonNull
+ public VirtualRotaryEncoderConfig build() {
+ return new VirtualRotaryEncoderConfig(this);
+ }
+ }
+}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.aidl
similarity index 87%
copy from services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
copy to core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.aidl
index bb8db8a..0b238ee 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.aidl
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package com.android.server.dreams;
+package android.hardware.input;
-public class TestDreamSettingsActivity {
-}
+parcelable VirtualRotaryEncoderScrollEvent;
diff --git a/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java
new file mode 100644
index 0000000..3be911abe7
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualRotaryEncoderScrollEvent.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.view.InputEvent;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * An event describing a rotary encoder scroll interaction originating from a remote device.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_VIRTUAL_ROTARY)
+@SystemApi
+public final class VirtualRotaryEncoderScrollEvent implements Parcelable {
+
+ private final float mScrollAmount;
+ private final long mEventTimeNanos;
+
+ private VirtualRotaryEncoderScrollEvent(float scrollAmount, long eventTimeNanos) {
+ mScrollAmount = scrollAmount;
+ mEventTimeNanos = eventTimeNanos;
+ }
+
+ private VirtualRotaryEncoderScrollEvent(@NonNull Parcel parcel) {
+ mScrollAmount = parcel.readFloat();
+ mEventTimeNanos = parcel.readLong();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+ parcel.writeFloat(mScrollAmount);
+ parcel.writeLong(mEventTimeNanos);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "VirtualRotaryScrollEvent("
+ + " scrollAmount=" + mScrollAmount
+ + " eventTime(ns)=" + mEventTimeNanos;
+ }
+
+ /**
+ * Returns the scroll amount, normalized from -1.0 to 1.0, inclusive.
+ * <p>
+ * Positive values indicate scrolling forward (e.g. down in a vertical list); negative values,
+ * backward.
+ * <p>
+ * Values of 1.0 or -1.0 represent the maximum supported scroll.
+ * </p>
+ */
+ public @FloatRange(from = -1.0f, to = 1.0f) float getScrollAmount() {
+ return mScrollAmount;
+ }
+
+ /**
+ * Returns the time this event occurred, in the {@link SystemClock#uptimeMillis()} time base but
+ * with nanosecond (instead of millisecond) precision.
+ *
+ * @see InputEvent#getEventTime()
+ */
+ public long getEventTimeNanos() {
+ return mEventTimeNanos;
+ }
+
+ /**
+ * Builder for {@link VirtualRotaryEncoderScrollEvent}.
+ */
+ public static final class Builder {
+
+ @FloatRange(from = -1.0f, to = 1.0f) private float mScrollAmount = 0.0f;
+ private long mEventTimeNanos = 0L;
+
+ /**
+ * Creates a {@link VirtualRotaryEncoderScrollEvent} object with the current configuration.
+ */
+ public @NonNull VirtualRotaryEncoderScrollEvent build() {
+ return new VirtualRotaryEncoderScrollEvent(mScrollAmount, mEventTimeNanos);
+ }
+
+ /**
+ * Sets the scroll amount, normalized from -1.0 to 1.0, inclusive.
+ * <p>
+ * Positive values indicate scrolling forward (e.g. down in a vertical list); negative
+ * values, backward.
+ * <p>
+ * Values of 1.0 or -1.0 represent the maximum supported scroll.
+ * </p>
+ * @return this builder, to allow for chaining of calls
+ */
+ public @NonNull Builder setScrollAmount(
+ @FloatRange(from = -1.0f, to = 1.0f) float scrollAmount) {
+ Preconditions.checkArgumentInRange(scrollAmount, -1f, 1f, "scrollAmount");
+ mScrollAmount = scrollAmount;
+ return this;
+ }
+
+ /**
+ * Sets the time (in nanoseconds) when this specific event was generated. This may be
+ * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
+ * millisecond), but can be different depending on the use case.
+ * This field is optional and can be omitted.
+ *
+ * @return this builder, to allow for chaining of calls
+ * @see InputEvent#getEventTime()
+ */
+ public @NonNull Builder setEventTimeNanos(long eventTimeNanos) {
+ if (eventTimeNanos < 0L) {
+ throw new IllegalArgumentException("Event time cannot be negative");
+ }
+ mEventTimeNanos = eventTimeNanos;
+ return this;
+ }
+ }
+
+ public static final @NonNull Creator<VirtualRotaryEncoderScrollEvent> CREATOR =
+ new Creator<VirtualRotaryEncoderScrollEvent>() {
+ public VirtualRotaryEncoderScrollEvent createFromParcel(Parcel source) {
+ return new VirtualRotaryEncoderScrollEvent(source);
+ }
+
+ public VirtualRotaryEncoderScrollEvent[] newArray(int size) {
+ return new VirtualRotaryEncoderScrollEvent[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index f9a2dbb..b1b4de3 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -63,48 +63,53 @@
}
void close() {
+ ProgramList programList;
synchronized (mLock) {
- if (mProgramList != null) {
- mProgramList.close();
+ if (mProgramList == null) {
+ return;
}
+ programList = mProgramList;
}
+ programList.close();
}
void setProgramListObserver(@Nullable ProgramList programList,
ProgramList.OnCloseListener closeListener) {
Objects.requireNonNull(closeListener, "CloseListener cannot be null");
+ ProgramList prevProgramList;
synchronized (mLock) {
- if (mProgramList != null) {
- Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
- mProgramList.close();
- }
+ prevProgramList = mProgramList;
mProgramList = programList;
- if (programList == null) {
- return;
- }
- programList.setOnCloseListener(() -> {
- synchronized (mLock) {
- if (mProgramList != programList) {
- return;
- }
- mProgramList = null;
- mLastCompleteList = null;
- }
- closeListener.onClose();
- });
- programList.addOnCompleteListener(() -> {
- synchronized (mLock) {
- if (mProgramList != programList) {
- return;
- }
- mLastCompleteList = programList.toList();
- if (mDelayedCompleteCallback) {
- Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
- sendBackgroundScanCompleteLocked();
- }
- }
- });
}
+ if (prevProgramList != null) {
+ Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
+ prevProgramList.close();
+ }
+ if (programList == null) {
+ return;
+ }
+ programList.setOnCloseListener(() -> {
+ synchronized (mLock) {
+ if (mProgramList != programList) {
+ return;
+ }
+ mProgramList = null;
+ mLastCompleteList = null;
+ }
+ closeListener.onClose();
+ });
+ programList.addOnCompleteListener(() -> {
+ synchronized (mLock) {
+ if (mProgramList != programList) {
+ return;
+ }
+ mLastCompleteList = programList.toList();
+ if (mDelayedCompleteCallback) {
+ Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
+ sendBackgroundScanCompleteLocked();
+ }
+ }
+ });
}
@Nullable List<RadioManager.ProgramInfo> getLastCompleteList() {
@@ -245,12 +250,14 @@
@Override
public void onProgramListUpdated(ProgramList.Chunk chunk) {
mHandler.post(() -> {
+ ProgramList programList;
synchronized (mLock) {
if (mProgramList == null) {
return;
}
- mProgramList.apply(Objects.requireNonNull(chunk, "Chunk cannot be null"));
+ programList = mProgramList;
}
+ programList.apply(Objects.requireNonNull(chunk, "Chunk cannot be null"));
});
}
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index feeef55..92ea0cf 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -3,6 +3,6 @@
include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
-per-file SSL*,Uri*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
+per-file SSL*,Uri*,Url* = prb@google.com,sorinbasca@google.com,narayan@google.com,ngeoffray@google.com
per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS
per-file Uri.java,Uri.aidl = varunshah@google.com
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 02f3a25..744f6a8 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -888,6 +888,13 @@
return (T) this;
}
+ @SuppressWarnings("unchecked")
+ @NonNull
+ public T addConsumedPowerForCustomComponent(int componentId, double componentPower) {
+ mPowerComponentsBuilder.addConsumedPowerForCustomComponent(componentId, componentPower);
+ return (T) this;
+ }
+
/**
* Sets the amount of time used by the specified component, e.g. CPU, WiFi etc.
*
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 90d82e7..61cc23d 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -870,6 +870,16 @@
}
/**
+ * Returns true if this Builder is configured to hold data for the specified
+ * custom power component ID.
+ */
+ public boolean isSupportedCustomPowerComponent(int componentId) {
+ return componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+ && componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+ + mBatteryConsumerDataLayout.customPowerComponentCount;
+ }
+
+ /**
* Constructs a read-only object using the Builder values.
*/
@NonNull
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 164e265..b035f12 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -516,6 +516,19 @@
}
@NonNull
+ public Builder addConsumedPowerForCustomComponent(int componentId, double componentPower) {
+ final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+ if (index < 0 || index >= mData.layout.customPowerComponentCount) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ mData.putDouble(mData.layout.firstCustomConsumedPowerColumn + index,
+ mData.getDouble(mData.layout.firstCustomConsumedPowerColumn + index)
+ + componentPower);
+ return this;
+ }
+
+ @NonNull
public Builder setUsageDurationMillis(BatteryConsumer.Key key,
long componentUsageDurationMillis) {
mData.putLong(key.mDurationColumnIndex, componentUsageDurationMillis);
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index f2143f6..7b91dd5 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -50,7 +50,7 @@
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
- mServiceManager = IServiceManager.Stub.asInterface(remote);
+ mServiceManager = IServiceManager.Stub.asInterface(this.getNativeServiceManager());
}
public IBinder asBinder() {
@@ -128,4 +128,6 @@
private IBinder mRemote;
private IServiceManager mServiceManager;
+
+ private native IBinder getNativeServiceManager();
}
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index cba4423..c801fabf 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -25,6 +25,8 @@
import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -63,7 +65,7 @@
mMemoryRegistration = new MemoryRegistration(mSize);
mCleaner = Cleaner.create(mFileDescriptor,
- new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
+ new Closer(mFileDescriptor, mMemoryRegistration));
}
/**
@@ -276,7 +278,6 @@
*/
@Override
public void close() {
- mFileDescriptor.setInt$(-1);
if (mCleaner != null) {
mCleaner.clean();
mCleaner = null;
@@ -326,21 +327,20 @@
* Cleaner that closes the FD
*/
private static final class Closer implements Runnable {
- private int mFd;
+ private FileDescriptor mFd;
private MemoryRegistration mMemoryReference;
- private Closer(int fd, MemoryRegistration memoryReference) {
+ private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
mFd = fd;
+ IoUtils.setFdOwner(mFd, this);
mMemoryReference = memoryReference;
}
@Override
public void run() {
- try {
- FileDescriptor fd = new FileDescriptor();
- fd.setInt$(mFd);
- Os.close(fd);
- } catch (ErrnoException e) { /* swallow error */ }
+ IoUtils.closeQuietly(mFd);
+ mFd = null;
+
mMemoryReference.release();
mMemoryReference = null;
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index e82c4b0..76cf901 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -201,3 +201,10 @@
description: "Tracing using Perfetto SDK."
bug: "303199244"
}
+
+flag {
+ name: "allow_consentless_bugreport_delegated_consent"
+ namespace: "crumpet"
+ description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"
+ bug: "324046728"
+}
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index eda755c..c73a422 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -31,3 +31,24 @@
description: "Enables the adaptive haptics feature"
bug: "305961689"
}
+
+flag {
+ namespace: "haptics"
+ name: "cancel_by_appops"
+ description: "Cancels ongoing vibrations when the appops mode changes to disallow them"
+ bug: "230745615"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "haptics"
+ name: "vibration_xml_apis"
+ description: "Enabled System APIs for vibration effect XML parser and serializer"
+ bug: "347273158"
+ metadata {
+ purpose: PURPOSE_FEATURE
+ }
+}
diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java
index a16d21e..e5543ab 100644
--- a/core/java/android/os/vibrator/persistence/ParsedVibration.java
+++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java
@@ -16,31 +16,35 @@
package android.os.vibrator.persistence;
+import static android.os.vibrator.Flags.FLAG_VIBRATION_XML_APIS;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
- * The result of parsing a serialized vibration, which can be define by one or more
- * {@link VibrationEffect} and a resolution method.
+ * The result of parsing a serialized vibration.
+ *
+ * @see VibrationXmlParser
*
* @hide
*/
-@TestApi
-@SuppressLint("UnflaggedApi") // @TestApi without associated feature.
-public class ParsedVibration {
+@TestApi // This was used in CTS before the flag was introduced.
+@SystemApi
+@FlaggedApi(FLAG_VIBRATION_XML_APIS)
+public final class ParsedVibration {
private final List<VibrationEffect> mEffects;
/** @hide */
+ @TestApi
public ParsedVibration(@NonNull List<VibrationEffect> effects) {
mEffects = effects;
}
@@ -49,40 +53,28 @@
public ParsedVibration(@NonNull VibrationEffect effect) {
mEffects = List.of(effect);
}
+
/**
* Returns the first parsed vibration supported by {@code vibrator}, or {@code null} if none of
* the parsed vibrations are supported.
*
* @hide
*/
- @TestApi
+ @TestApi // This was used in CTS before the flag was introduced.
+ @SystemApi
+ @FlaggedApi(FLAG_VIBRATION_XML_APIS)
@Nullable
public VibrationEffect resolve(@NonNull Vibrator vibrator) {
return resolve(vibrator.getInfo());
}
/**
- * Returns the parsed vibrations for testing purposes.
- *
- * <p>Real callers should not use this method. Instead, they should resolve to a
- * {@link VibrationEffect} via {@link #resolve(Vibrator)}.
- *
- * @hide
- */
- @TestApi
- @VisibleForTesting
- @NonNull
- public List<VibrationEffect> getVibrationEffects() {
- return Collections.unmodifiableList(mEffects);
- }
-
- /**
* Same as {@link #resolve(Vibrator)}, but uses {@link VibratorInfo} instead for resolving.
*
* @hide
*/
@Nullable
- public final VibrationEffect resolve(@NonNull VibratorInfo info) {
+ public VibrationEffect resolve(@NonNull VibratorInfo info) {
for (int i = 0; i < mEffects.size(); i++) {
VibrationEffect effect = mEffects.get(i);
if (info.areVibrationFeaturesSupported(effect)) {
@@ -91,4 +83,21 @@
}
return null;
}
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ParsedVibration)) {
+ return false;
+ }
+ ParsedVibration other = (ParsedVibration) o;
+ return mEffects.equals(other.mEffects);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mEffects);
+ }
}
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index 7202d9a..e2312e0 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -16,13 +16,15 @@
package android.os.vibrator.persistence;
+import static android.os.vibrator.Flags.FLAG_VIBRATION_XML_APIS;
+
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.VibrationEffect;
-import android.util.Slog;
import android.util.Xml;
import com.android.internal.vibrator.persistence.VibrationEffectXmlParser;
@@ -36,9 +38,12 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -116,10 +121,10 @@
*
* @hide
*/
-@TestApi
-@SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+@TestApi // This was used in CTS before the flag was introduced.
+@SystemApi
+@FlaggedApi(FLAG_VIBRATION_XML_APIS)
public final class VibrationXmlParser {
- private static final String TAG = "VibrationXmlParser";
/**
* The MIME type for a xml holding a vibration.
@@ -168,55 +173,12 @@
}
/**
- * Parses XML content from given input stream into a {@link VibrationEffect}.
- *
- * <p>This method parses an XML content that contains a single, complete {@link VibrationEffect}
- * serialization. As such, the root tag must be a "vibration" tag.
- *
- * <p>This parser fails silently and returns {@code null} if the content of the input stream
- * does not follow the schema or has unsupported values.
- *
- * @return the {@link VibrationEffect} if parsed successfully, {@code null} otherwise.
- * @throws IOException error reading from given {@link Reader}
- *
- * @hide
- */
- @TestApi
- @Nullable
- public static VibrationEffect parseVibrationEffect(@NonNull Reader reader) throws IOException {
- return parseVibrationEffect(reader, /* flags= */ 0);
- }
-
- /**
- * Parses XML content from given input stream into a {@link VibrationEffect}.
- *
- * <p>This method parses an XML content that contains a single, complete {@link VibrationEffect}
- * serialization. As such, the root tag must be a "vibration" tag.
- *
- * <p>Same as {@link #parseVibrationEffect(Reader)}, with extra flags to control the parsing
- * behavior.
- *
- * @hide
- */
- @Nullable
- public static VibrationEffect parseVibrationEffect(@NonNull Reader reader, @Flags int flags)
- throws IOException {
- try {
- return parseDocumentInternal(
- reader, flags, VibrationXmlParser::parseVibrationEffectInternal);
- } catch (XmlParserException | XmlPullParserException e) {
- Slog.w(TAG, "Error parsing vibration XML", e);
- return null;
- }
- }
-
- /**
* Parses XML content from given input stream into a {@link ParsedVibration}.
*
- * <p>It supports both the "vibration" and "vibration-select" root tags.
+ * <p>It supports both the "vibration-effect" and "vibration-select" root tags.
* <ul>
- * <li>If "vibration" is the root tag, the serialization provided through {@code reader}
- * should contain a valid serialization for a single vibration.
+ * <li>If "vibration-effect" is the root tag, the serialization provided should contain a
+ * valid serialization for a single vibration.
* <li>If "vibration-select" is the root tag, the serialization may contain one or more
* valid vibration serializations.
* </ul>
@@ -225,36 +187,95 @@
* vibration(s), and the caller can get a concrete {@link VibrationEffect} by resolving this
* result to a specific vibrator.
*
- * <p>This parser fails silently and returns {@code null} if the content of the input does not
- * follow the schema or has unsupported values.
+ * <p>This parser fails with an exception if the content of the input stream does not follow the
+ * schema or has unsupported values.
*
* @return a {@link ParsedVibration}
- * @throws IOException error reading from given {@link Reader}
+ * @throws IOException error reading from given {@link InputStream} or parsing the content.
*
* @hide
*/
- @TestApi
- @Nullable
+ @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced.
+ @SystemApi
+ @FlaggedApi(FLAG_VIBRATION_XML_APIS)
+ @NonNull
+ public static ParsedVibration parse(@NonNull InputStream inputStream) throws IOException {
+ return parseDocument(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Parses XML content from given input stream into a single {@link VibrationEffect}.
+ *
+ * <p>This method parses an XML content that contains a single, complete {@link VibrationEffect}
+ * serialization. As such, the root tag must be a "vibration-effect" tag.
+ *
+ * <p>This parser fails with an exception if the content of the input stream does not follow the
+ * schema or has unsupported values.
+ *
+ * @return the parsed {@link VibrationEffect}
+ * @throws IOException error reading from given {@link InputStream} or parsing the content.
+ *
+ * @hide
+ */
+ @TestApi // Replacing test APIs used in CTS before the flagged system APIs was introduced.
+ @SystemApi
+ @FlaggedApi(FLAG_VIBRATION_XML_APIS)
+ @NonNull
+ public static VibrationEffect parseVibrationEffect(@NonNull InputStream inputStream)
+ throws IOException {
+ return parseVibrationEffect(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Parses XML content from given {@link Reader} into a {@link VibrationEffect}.
+ *
+ * <p>Same as {@link #parseVibrationEffect(InputStream)}, but with a {@link Reader}.
+ *
+ * @hide
+ */
+ @NonNull
+ public static VibrationEffect parseVibrationEffect(@NonNull Reader reader) throws IOException {
+ return parseVibrationEffect(reader, /* flags= */ 0);
+ }
+
+ /**
+ * Parses XML content from given {@link Reader} into a {@link VibrationEffect}.
+ *
+ * <p>Same as {@link #parseVibrationEffect(Reader)}, with extra flags to control the parsing
+ * behavior.
+ *
+ * @hide
+ */
+ @NonNull
+ public static VibrationEffect parseVibrationEffect(@NonNull Reader reader, @Flags int flags)
+ throws IOException {
+ return parseDocumentInternal(reader, flags,
+ VibrationXmlParser::parseVibrationEffectInternal);
+ }
+
+ /**
+ * Parses XML content from given {@link Reader} into a {@link ParsedVibration}.
+ *
+ * <p>Same as {@link #parse(InputStream)}, but with a {@link Reader}.
+ *
+ * @hide
+ */
+ @NonNull
public static ParsedVibration parseDocument(@NonNull Reader reader) throws IOException {
return parseDocument(reader, /* flags= */ 0);
}
/**
- * Parses XML content from given input stream into a {@link ParsedVibration}.
+ * Parses XML content from given {@link Reader} into a {@link ParsedVibration}.
*
* <p>Same as {@link #parseDocument(Reader)}, with extra flags to control the parsing behavior.
*
* @hide
*/
- @Nullable
+ @NonNull
public static ParsedVibration parseDocument(@NonNull Reader reader, @Flags int flags)
throws IOException {
- try {
- return parseDocumentInternal(reader, flags, VibrationXmlParser::parseElementInternal);
- } catch (XmlParserException | XmlPullParserException e) {
- Slog.w(TAG, "Error parsing vibration/vibration-select XML", e);
- return null;
- }
+ return parseDocumentInternal(reader, flags, VibrationXmlParser::parseElementInternal);
}
/**
@@ -262,7 +283,7 @@
* {@link ParsedVibration}.
*
* <p>Same as {@link #parseDocument(Reader, int)}, but, instead of parsing the full XML content,
- * it takes a parser that points to either a <vibration-effect> or a <vibration-select> start
+ * it takes a parser that points to either a "vibration-effect" or a "vibration-select" start
* tag. No other parser position, including start of document, is considered valid.
*
* <p>This method parses until an end "vibration-effect" or "vibration-select" tag (depending
@@ -270,37 +291,22 @@
* will point to the end tag.
*
* @throws IOException error parsing from given {@link TypedXmlPullParser}.
- * @throws VibrationXmlParserException if the XML tag cannot be parsed into a
- * {@link ParsedVibration}. The given {@code parser} might be pointing to a child XML tag
- * that caused the parser failure.
+ * The given {@code parser} might be pointing to a child XML tag that caused the parser
+ * failure.
*
* @hide
*/
@NonNull
public static ParsedVibration parseElement(@NonNull TypedXmlPullParser parser, @Flags int flags)
- throws IOException, VibrationXmlParserException {
+ throws IOException {
try {
return parseElementInternal(parser, flags);
} catch (XmlParserException e) {
- throw new VibrationXmlParserException("Error parsing vibration-select.", e);
+ throw new ParseFailedException(e);
}
}
- /**
- * Represents an error while parsing a vibration XML input.
- *
- * @hide
- */
- public static final class VibrationXmlParserException extends Exception {
- private VibrationXmlParserException(String message, Throwable cause) {
- super(message, cause);
- }
-
- private VibrationXmlParserException(String message) {
- super(message);
- }
- }
-
+ @NonNull
private static ParsedVibration parseElementInternal(
@NonNull TypedXmlPullParser parser, @Flags int flags)
throws IOException, XmlParserException {
@@ -313,11 +319,12 @@
case XmlConstants.TAG_VIBRATION_SELECT:
return parseVibrationSelectInternal(parser, flags);
default:
- throw new XmlParserException(
- "Unexpected tag name when parsing element: " + tagName);
+ throw new ParseFailedException(
+ "Unexpected tag " + tagName + " when parsing a vibration");
}
}
+ @NonNull
private static ParsedVibration parseVibrationSelectInternal(
@NonNull TypedXmlPullParser parser, @Flags int flags)
throws IOException, XmlParserException {
@@ -332,7 +339,7 @@
return new ParsedVibration(effects);
}
- /** Parses a single XML element for "vibration" tag into a {@link VibrationEffect}. */
+ @NonNull
private static VibrationEffect parseVibrationEffectInternal(
@NonNull TypedXmlPullParser parser, @Flags int flags)
throws IOException, XmlParserException {
@@ -347,32 +354,60 @@
* This method parses a whole XML document (provided through a {@link Reader}). The root tag is
* parsed as per a provided {@link ElementParser}.
*/
+ @NonNull
private static <T> T parseDocumentInternal(
@NonNull Reader reader, @Flags int flags, ElementParser<T> parseLogic)
- throws IOException, XmlParserException, XmlPullParserException {
- TypedXmlPullParser parser = Xml.newFastPullParser();
- parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(reader);
+ throws IOException {
+ try {
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(reader);
- // Ensure XML starts with a document start tag.
- XmlReader.readDocumentStart(parser);
+ // Ensure XML starts with a document start tag.
+ XmlReader.readDocumentStart(parser);
- // Parse root tag.
- T result = parseLogic.parse(parser, flags);
+ // Parse root tag.
+ T result = parseLogic.parse(parser, flags);
- // Ensure XML ends after root tag is consumed.
- XmlReader.readDocumentEndTag(parser);
+ // Ensure XML ends after root tag is consumed.
+ XmlReader.readDocumentEndTag(parser);
- return result;
+ return result;
+ } catch (XmlPullParserException e) {
+ throw new ParseFailedException("Error initializing XMLPullParser", e);
+ } catch (XmlParserException e) {
+ throw new ParseFailedException(e);
+ }
}
/** Encapsulate a logic to parse an XML element from an open parser. */
private interface ElementParser<T> {
/** Parses a single XML element starting from the current position of the {@code parser}. */
+ @NonNull
T parse(@NonNull TypedXmlPullParser parser, @Flags int flags)
throws IOException, XmlParserException;
}
+ /**
+ * Represents an error while parsing a vibration XML input.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final class ParseFailedException extends IOException {
+ private ParseFailedException(String message) {
+ super(message);
+ }
+
+ private ParseFailedException(XmlParserException parserException) {
+ this(parserException.getMessage(), parserException);
+ }
+
+ private ParseFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
private VibrationXmlParser() {
}
}
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index 2065d5d..a26c6f4 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -18,9 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
import android.annotation.TestApi;
-import android.os.CombinedVibration;
import android.os.VibrationEffect;
import android.util.Xml;
@@ -37,14 +35,13 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Serializes {@link CombinedVibration} and {@link VibrationEffect} instances to XML.
+ * Serializes {@link VibrationEffect} instances to XML.
*
* <p>This uses the same schema expected by the {@link VibrationXmlParser}.
*
* @hide
*/
@TestApi
-@SuppressLint("UnflaggedApi") // @TestApi without associated feature.
public final class VibrationXmlSerializer {
/**
@@ -80,20 +77,19 @@
"http://xmlpull.org/v1/doc/features.html#indent-output";
/**
- * Serializes a {@link VibrationEffect} to XML and writes output to given {@link Writer}.
+ * Serializes a {@link VibrationEffect} to XML and writes output to given {@link Writer} using
+ * UTF-8 encoding.
*
- * <p>This method will only write into the {@link Writer} if the effect can successfully
- * be represented by the XML serialization. It will throw an exception otherwise.
+ * <p>This method will only write to the stream if the effect can successfully be represented by
+ * the XML serialization. It will throw an exception otherwise.
*
- * @throws SerializationFailedException serialization of input effect failed, no data was
- * written into given {@link Writer}.
- * @throws IOException error writing to given {@link Writer}.
+ * @throws IOException serialization of input effect failed or error writing to output stream.
*
* @hide
*/
@TestApi
public static void serialize(@NonNull VibrationEffect effect, @NonNull Writer writer)
- throws SerializationFailedException, IOException {
+ throws IOException {
serialize(effect, writer, /* flags= */ 0);
}
@@ -106,7 +102,7 @@
* @hide
*/
public static void serialize(@NonNull VibrationEffect effect, @NonNull Writer writer,
- @Flags int flags) throws SerializationFailedException, IOException {
+ @Flags int flags) throws IOException {
// Serialize effect first to fail early.
XmlSerializedVibration<VibrationEffect> serializedVibration =
toSerializedVibration(effect, flags);
@@ -138,17 +134,16 @@
}
/**
- * Exception thrown when a {@link VibrationEffect} instance serialization fails.
+ * Exception thrown when a {@link VibrationEffect} serialization fails.
*
* <p>The serialization can fail if a given vibration cannot be represented using the public
- * format, or if it uses hidden APIs that are not supported for serialization (e.g.
- * {@link VibrationEffect.WaveformBuilder}).
+ * format, or if it uses a non-public representation that is not supported for serialization.
*
* @hide
*/
@TestApi
- public static final class SerializationFailedException extends RuntimeException {
- SerializationFailedException(VibrationEffect effect, Throwable cause) {
+ public static final class SerializationFailedException extends IOException {
+ private SerializationFailedException(VibrationEffect effect, Throwable cause) {
super("Serialization failed for vibration effect " + effect, cause);
}
}
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 141ffc9..a698b18 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -374,20 +374,22 @@
public @NonNull List<PermissionGroupUsage> getOpUsageDataForAllDevices(
boolean includeMicrophoneUsage) {
List<PermissionGroupUsage> allUsages = new ArrayList<>();
- List<VirtualDevice> virtualDevices = mVirtualDeviceManager.getVirtualDevices();
- ArraySet<String> persistentDeviceIds = new ArraySet<>();
- for (int num = 0; num < virtualDevices.size(); num++) {
- persistentDeviceIds.add(virtualDevices.get(num).getPersistentDeviceId());
+ if (mVirtualDeviceManager != null) {
+ List<VirtualDevice> virtualDevices = mVirtualDeviceManager.getVirtualDevices();
+ ArraySet<String> persistentDeviceIds = new ArraySet<>();
+
+ for (int num = 0; num < virtualDevices.size(); num++) {
+ persistentDeviceIds.add(virtualDevices.get(num).getPersistentDeviceId());
+ }
+ persistentDeviceIds.add(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+
+ for (int index = 0; index < persistentDeviceIds.size(); index++) {
+ allUsages.addAll(
+ getOpUsageDataByDevice(includeMicrophoneUsage,
+ persistentDeviceIds.valueAt(index)));
+ }
}
- persistentDeviceIds.add(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
-
- for (int index = 0; index < persistentDeviceIds.size(); index++) {
- allUsages.addAll(
- getOpUsageDataByDevice(includeMicrophoneUsage,
- persistentDeviceIds.valueAt(index)));
- }
-
return allUsages;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d91508f..9682d36 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11046,6 +11046,12 @@
public static final String BIOMETRIC_APP_ENABLED = "biometric_app_enabled";
/**
+ * Whether or not mandatory biometrics is enabled.
+ * @hide
+ */
+ public static final String MANDATORY_BIOMETRICS = "mandatory_biometrics";
+
+ /**
* Whether or not active unlock triggers on wake.
* @hide
*/
@@ -11066,6 +11072,13 @@
"active_unlock_on_biometric_fail";
/**
+ * Whether or not active unlock triggers on legacy unlock intents.
+ * @hide
+ */
+ public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY =
+ "active_unlock_on_unlock_intent_legacy";
+
+ /**
* If active unlock triggers on biometric failures, include the following error codes
* as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}.
* Error codes should be separated by a pipe. For example: "1|4|5". If active unlock
@@ -20081,7 +20094,7 @@
* (0 = false, 1 = true)
* @hide
*/
- @Readable(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Readable
public static final String REDUCE_MOTION = "reduce_motion";
/**
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index fe7eab7..3954bc2 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -27,14 +27,6 @@
}
flag {
- name: "fix_unlocked_device_required_keys_v2"
- namespace: "hardware_backed_security"
- description: "Fix bugs in behavior of UnlockedDeviceRequired keystore keys"
- bug: "296464083"
- is_fixed_read_only: true
-}
-
-flag {
name: "keyinfo_unlocked_device_required"
is_exported: true
namespace: "hardware_backed_security"
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index d6425c3..82c1613 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -2,14 +2,6 @@
container: "system"
flag {
- name: "chooser_album_text"
- is_exported: true
- namespace: "intentresolver"
- description: "Flag controlling the album text subtype hint for sharesheet"
- bug: "323380224"
-}
-
-flag {
name: "enable_sharesheet_metadata_extra"
is_exported: true
namespace: "intentresolver"
@@ -32,3 +24,14 @@
description: "Provides additional callbacks with information about user actions in ChooserResult"
bug: "263474465"
}
+
+flag {
+ name: "fix_resolver_memory_leak"
+ is_exported: true
+ namespace: "intentresolver"
+ description: "ResolverActivity memory leak (through the AppPredictor callback) fix"
+ bug: "346671041"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/service/dreams/DreamOverlayConnectionHandler.java b/core/java/android/service/dreams/DreamOverlayConnectionHandler.java
index 85a13c7..bc03400 100644
--- a/core/java/android/service/dreams/DreamOverlayConnectionHandler.java
+++ b/core/java/android/service/dreams/DreamOverlayConnectionHandler.java
@@ -27,7 +27,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ObservableServiceConnection;
-import com.android.internal.util.PersistentServiceConnection;
import java.util.ArrayList;
import java.util.List;
@@ -48,22 +47,20 @@
private static final int MSG_OVERLAY_CLIENT_READY = 3;
private final Handler mHandler;
- private final PersistentServiceConnection<IDreamOverlay> mConnection;
+ private final ObservableServiceConnection<IDreamOverlay> mConnection;
// Retrieved Client
private IDreamOverlayClient mClient;
// A list of pending requests to execute on the overlay.
private final List<Consumer<IDreamOverlayClient>> mConsumers = new ArrayList<>();
private final OverlayConnectionCallback mCallback;
+ private final Runnable mOnDisconnected;
DreamOverlayConnectionHandler(
Context context,
Looper looper,
Intent serviceIntent,
- int minConnectionDurationMs,
- int maxReconnectAttempts,
- int baseReconnectDelayMs) {
- this(context, looper, serviceIntent, minConnectionDurationMs, maxReconnectAttempts,
- baseReconnectDelayMs, new Injector());
+ Runnable onDisconnected) {
+ this(context, looper, serviceIntent, onDisconnected, new Injector());
}
@VisibleForTesting
@@ -71,20 +68,15 @@
Context context,
Looper looper,
Intent serviceIntent,
- int minConnectionDurationMs,
- int maxReconnectAttempts,
- int baseReconnectDelayMs,
+ Runnable onDisconnected,
Injector injector) {
mCallback = new OverlayConnectionCallback();
mHandler = new Handler(looper, new OverlayHandlerCallback());
+ mOnDisconnected = onDisconnected;
mConnection = injector.buildConnection(
context,
mHandler,
- serviceIntent,
- minConnectionDurationMs,
- maxReconnectAttempts,
- baseReconnectDelayMs
- );
+ serviceIntent);
}
/**
@@ -201,10 +193,14 @@
@Override
public void onDisconnected(ObservableServiceConnection<IDreamOverlay> connection,
int reason) {
+ Log.i(TAG, "Dream overlay disconnected, reason: " + reason);
mClient = null;
// Cancel any pending messages about the overlay being ready, since it is no
// longer ready.
mHandler.removeMessages(MSG_OVERLAY_CLIENT_READY);
+ if (mOnDisconnected != null) {
+ mOnDisconnected.run();
+ }
}
}
@@ -217,25 +213,18 @@
* Returns milliseconds since boot, not counting time spent in deep sleep. Can be overridden
* in tests with a fake clock.
*/
- public PersistentServiceConnection<IDreamOverlay> buildConnection(
+ public ObservableServiceConnection<IDreamOverlay> buildConnection(
Context context,
Handler handler,
- Intent serviceIntent,
- int minConnectionDurationMs,
- int maxReconnectAttempts,
- int baseReconnectDelayMs) {
+ Intent serviceIntent) {
final Executor executor = handler::post;
final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
- return new PersistentServiceConnection<>(
+ return new ObservableServiceConnection<>(
context,
executor,
- handler,
IDreamOverlay.Stub::asInterface,
serviceIntent,
- flags,
- minConnectionDurationMs,
- maxReconnectAttempts,
- baseReconnectDelayMs
+ flags
);
}
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 6efca0c..8ecb1fb 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -182,6 +182,7 @@
/**
* The name of the dream manager service.
+ *
* @hide
*/
public static final String DREAM_SERVICE = "dreams";
@@ -222,12 +223,14 @@
/**
* Dream category for Low Light Dream
+ *
* @hide
*/
public static final int DREAM_CATEGORY_LOW_LIGHT = 1 << 0;
/**
* Dream category for Home Panel Dream
+ *
* @hide
*/
public static final int DREAM_CATEGORY_HOME_PANEL = 1 << 1;
@@ -295,7 +298,8 @@
void init(Context context);
/** Creates and returns the dream overlay connection */
- DreamOverlayConnectionHandler createOverlayConnection(ComponentName overlayComponent);
+ DreamOverlayConnectionHandler createOverlayConnection(ComponentName overlayComponent,
+ Runnable onDisconnected);
/** Returns the {@link DreamActivity} component */
ComponentName getDreamActivityComponent();
@@ -333,16 +337,15 @@
@Override
public DreamOverlayConnectionHandler createOverlayConnection(
- ComponentName overlayComponent) {
+ ComponentName overlayComponent,
+ Runnable onDisconnected) {
final Resources resources = mContext.getResources();
return new DreamOverlayConnectionHandler(
/* context= */ mContext,
Looper.getMainLooper(),
new Intent().setComponent(overlayComponent),
- resources.getInteger(R.integer.config_minDreamOverlayDurationMs),
- resources.getInteger(R.integer.config_dreamOverlayMaxReconnectAttempts),
- resources.getInteger(R.integer.config_dreamOverlayReconnectTimeoutMs));
+ onDisconnected);
}
@Override
@@ -738,7 +741,7 @@
* @see View#findViewById(int)
* @see DreamService#requireViewById(int)
*/
- @Nullable
+ /* TODO(b/347672184): Re-add @Nullable */
public <T extends View> T findViewById(@IdRes int id) {
return getWindow().findViewById(id);
}
@@ -1176,7 +1179,8 @@
// Connect to the overlay service if present.
if (!mWindowless && overlayComponent != null) {
- mOverlayConnection = mInjector.createOverlayConnection(overlayComponent);
+ mOverlayConnection = mInjector.createOverlayConnection(overlayComponent,
+ this::finish);
if (!mOverlayConnection.bind()) {
// Binding failed.
@@ -1389,8 +1393,7 @@
convertToComponentName(
rawMetadata.getString(
com.android.internal.R.styleable.Dream_settingsActivity),
- serviceInfo,
- packageManager),
+ serviceInfo),
rawMetadata.getDrawable(
com.android.internal.R.styleable.Dream_previewImage),
rawMetadata.getBoolean(R.styleable.Dream_showClockAndComplications,
@@ -1405,38 +1408,26 @@
}
@Nullable
- private static ComponentName convertToComponentName(
- @Nullable String flattenedString,
- ServiceInfo serviceInfo,
- PackageManager packageManager) {
+ private static ComponentName convertToComponentName(@Nullable String flattenedString,
+ ServiceInfo serviceInfo) {
if (flattenedString == null) {
return null;
}
- final ComponentName cn =
- flattenedString.contains("/")
- ? ComponentName.unflattenFromString(flattenedString)
- : new ComponentName(serviceInfo.packageName, flattenedString);
-
- if (cn == null) {
- return null;
+ if (!flattenedString.contains("/")) {
+ return new ComponentName(serviceInfo.packageName, flattenedString);
}
// Ensure that the component is from the same package as the dream service. If not,
// treat the component as invalid and return null instead.
+ final ComponentName cn = ComponentName.unflattenFromString(flattenedString);
+ if (cn == null) return null;
if (!cn.getPackageName().equals(serviceInfo.packageName)) {
Log.w(TAG,
"Inconsistent package name in component: " + cn.getPackageName()
+ ", should be: " + serviceInfo.packageName);
return null;
}
-
- // Ensure that the activity exists. If not, treat the component as invalid and return null.
- if (new Intent().setComponent(cn).resolveActivityInfo(packageManager, 0) == null) {
- Log.w(TAG, "Dream settings activity not found: " + cn);
- return null;
- }
-
return cn;
}
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 9c14946..073f512 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -15,6 +15,8 @@
*/
package android.service.notification;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
@@ -62,7 +64,8 @@
KEY_IMPORTANCE_PROPOSAL,
KEY_SENSITIVE_CONTENT,
KEY_RANKING_SCORE,
- KEY_NOT_CONVERSATION
+ KEY_NOT_CONVERSATION,
+ KEY_TYPE
})
@Retention(RetentionPolicy.SOURCE)
public @interface Keys {}
@@ -172,6 +175,54 @@
public static final String KEY_NOT_CONVERSATION = "key_not_conversation";
/**
+ * Data type: int, the classification type of this notification. The OS may display
+ * notifications differently depending on the type, and may change the alerting level of the
+ * notification.
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final String KEY_TYPE = "key_type";
+
+ /** @hide */
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_OTHER,
+ TYPE_PROMOTION,
+ TYPE_SOCIAL_MEDIA,
+ TYPE_NEWS,
+ TYPE_CONTENT_RECOMMENDATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Types {}
+
+ /**
+ * This notification can be categorized, but not into one of the other categories known to the
+ * OS at a given version.
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final int TYPE_OTHER = 0;
+ /**
+ * The type of this notification is a promotion/deal.
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final int TYPE_PROMOTION = 1;
+ /**
+ * The type of this notification is social media content that isn't a
+ * {@link Notification.Builder#setShortcutId(String) conversation}.
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final int TYPE_SOCIAL_MEDIA = 2;
+ /**
+ * The type of this notification is news.
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final int TYPE_NEWS = 3;
+ /**
+ * The type of this notification is content recommendation, for example new videos or books the
+ * user may be interested in.
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public static final int TYPE_CONTENT_RECOMMENDATION = 4;
+
+ /**
* Create a notification adjustment.
*
* @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 7a0c016..863a99a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -262,15 +262,12 @@
private static final String CONDITION_ATT_SOURCE = "source";
private static final String CONDITION_ATT_FLAGS = "flags";
- private static final String ZEN_POLICY_TAG = "zen_policy";
-
private static final String MANUAL_TAG = "manual";
private static final String AUTOMATIC_TAG = "automatic";
private static final String AUTOMATIC_DELETED_TAG = "deleted";
private static final String RULE_ATT_ID = "ruleId";
private static final String RULE_ATT_ENABLED = "enabled";
- private static final String RULE_ATT_SNOOZING = "snoozing";
private static final String RULE_ATT_NAME = "name";
private static final String RULE_ATT_PKG = "pkg";
private static final String RULE_ATT_COMPONENT = "component";
@@ -286,6 +283,7 @@
private static final String RULE_ATT_ICON = "rule_icon";
private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc";
private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant";
+ private static final String RULE_ATT_DISABLED_ORIGIN = "disabledOrigin";
private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale";
private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY =
@@ -1170,6 +1168,10 @@
if (deletionInstant != null) {
rt.deletionInstant = Instant.ofEpochMilli(deletionInstant);
}
+ if (Flags.modesUi()) {
+ rt.disabledOrigin = safeInt(parser, RULE_ATT_DISABLED_ORIGIN,
+ UPDATE_ORIGIN_UNKNOWN);
+ }
}
return rt;
}
@@ -1224,6 +1226,9 @@
out.attributeLong(null, RULE_ATT_DELETION_INSTANT,
rule.deletionInstant.toEpochMilli());
}
+ if (Flags.modesUi()) {
+ out.attributeInt(null, RULE_ATT_DISABLED_ORIGIN, rule.disabledOrigin);
+ }
}
}
@@ -2373,7 +2378,7 @@
public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user
public String calName; // CalendarContract.Calendars.DISPLAY_NAME, or null for any
- public Long calendarId; // Calendars._ID, or null if restored from < Q calendar
+ @Nullable public Long calendarId; // Calendars._ID, or null if restored from < Q calendar
public int reply;
@Override
@@ -2405,6 +2410,33 @@
}
}
+ // ==== Built-in system condition: custom manual ====
+
+ public static final String CUSTOM_MANUAL_PATH = "custom_manual";
+ private static final Uri CUSTOM_MANUAL_CONDITION_ID =
+ new Uri.Builder().scheme(Condition.SCHEME)
+ .authority(SYSTEM_AUTHORITY)
+ .appendPath(CUSTOM_MANUAL_PATH)
+ .build();
+
+ /** Returns the condition id used for manual (not automatically triggered) custom rules. */
+ public static Uri toCustomManualConditionId() {
+ return CUSTOM_MANUAL_CONDITION_ID;
+ }
+
+ /**
+ * Returns whether the supplied {@link Uri} corresponds to the condition id used for manual (not
+ * automatically triggered) custom rules.
+ */
+ public static boolean isValidCustomManualConditionId(Uri conditionId) {
+ return CUSTOM_MANUAL_CONDITION_ID.equals(conditionId);
+ }
+
+ /** Returns the {@link ComponentName} of the custom manual condition provider. */
+ public static ComponentName getCustomManualConditionProvider() {
+ return new ComponentName(SYSTEM_AUTHORITY, "CustomManualConditionProvider");
+ }
+
// ==== End built-in system conditions ====
private static int[] tryParseHourAndMinute(String value) {
@@ -2487,6 +2519,8 @@
@ZenPolicy.ModifiableField public int zenPolicyUserModifiedFields;
@ZenDeviceEffects.ModifiableField public int zenDeviceEffectsUserModifiedFields;
@Nullable public Instant deletionInstant; // Only set on deleted rules.
+ @FlaggedApi(Flags.FLAG_MODES_UI)
+ @ConfigChangeOrigin public int disabledOrigin = UPDATE_ORIGIN_UNKNOWN;
public ZenRule() { }
@@ -2525,6 +2559,9 @@
if (source.readInt() == 1) {
deletionInstant = Instant.ofEpochMilli(source.readLong());
}
+ if (Flags.modesUi()) {
+ disabledOrigin = source.readInt();
+ }
}
}
@@ -2599,6 +2636,9 @@
} else {
dest.writeInt(0);
}
+ if (Flags.modesUi()) {
+ dest.writeInt(disabledOrigin);
+ }
}
}
@@ -2644,6 +2684,9 @@
if (deletionInstant != null) {
sb.append(",deletionInstant=").append(deletionInstant);
}
+ if (Flags.modesUi()) {
+ sb.append(",disabledOrigin=").append(disabledOrigin);
+ }
}
return sb.append(']').toString();
@@ -2697,7 +2740,7 @@
&& other.modified == modified;
if (Flags.modesApi()) {
- return finalEquals
+ finalEquals = finalEquals
&& Objects.equals(other.zenDeviceEffects, zenDeviceEffects)
&& other.allowManualInvocation == allowManualInvocation
&& Objects.equals(other.iconResName, iconResName)
@@ -2708,6 +2751,11 @@
&& other.zenDeviceEffectsUserModifiedFields
== zenDeviceEffectsUserModifiedFields
&& Objects.equals(other.deletionInstant, deletionInstant);
+
+ if (Flags.modesUi()) {
+ finalEquals = finalEquals
+ && other.disabledOrigin == disabledOrigin;
+ }
}
return finalEquals;
@@ -2716,11 +2764,21 @@
@Override
public int hashCode() {
if (Flags.modesApi()) {
- return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component, configurationActivity, pkg, id, enabler, zenPolicy,
- zenDeviceEffects, modified, allowManualInvocation, iconResName,
- triggerDescription, type, userModifiedFields, zenPolicyUserModifiedFields,
- zenDeviceEffectsUserModifiedFields, deletionInstant);
+ if (Flags.modesUi()) {
+ return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
+ component, configurationActivity, pkg, id, enabler, zenPolicy,
+ zenDeviceEffects, modified, allowManualInvocation, iconResName,
+ triggerDescription, type, userModifiedFields,
+ zenPolicyUserModifiedFields,
+ zenDeviceEffectsUserModifiedFields, deletionInstant, disabledOrigin);
+ } else {
+ return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
+ component, configurationActivity, pkg, id, enabler, zenPolicy,
+ zenDeviceEffects, modified, allowManualInvocation, iconResName,
+ triggerDescription, type, userModifiedFields,
+ zenPolicyUserModifiedFields,
+ zenDeviceEffectsUserModifiedFields, deletionInstant);
+ }
}
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index c5b4b41..bdef041 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -35,4 +35,12 @@
description: "Guards the new CallStyleNotificationEventsCallback"
bug: "305095040"
is_fixed_read_only: true
+}
+
+flag {
+ name: "notification_classification"
+ is_exported: true
+ namespace: "systemui"
+ description: "Allows the NAS to classify notifications"
+ bug: "343988084"
}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 8271caf..3161ff1 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -31,7 +31,6 @@
import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
import static com.android.window.flags.Flags.noVisibilityEventOnDisplayStateChange;
import static com.android.window.flags.Flags.offloadColorExtraction;
-import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
import android.animation.AnimationHandler;
import android.animation.Animator;
@@ -302,13 +301,10 @@
final InsetsState mInsetsState = new InsetsState();
final InsetsSourceControl.Array mTempControls = new InsetsSourceControl.Array();
final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
- final Bundle mSyncSeqIdBundle = windowSessionRelayoutInfo() ? null : new Bundle();
SurfaceControl mSurfaceControl = new SurfaceControl();
- WindowRelayoutResult mRelayoutResult = windowSessionRelayoutInfo()
- ? new WindowRelayoutResult(mWinFrames, mMergedConfiguration, mSurfaceControl,
- mInsetsState, mTempControls)
- : null;
+ WindowRelayoutResult mRelayoutResult = new WindowRelayoutResult(
+ mWinFrames, mMergedConfiguration, mSurfaceControl, mInsetsState, mTempControls);
private final Point mSurfaceSize = new Point();
private final Point mLastSurfaceSize = new Point();
@@ -1277,15 +1273,8 @@
} else {
mLayout.surfaceInsets.set(0, 0, 0, 0);
}
- final int relayoutResult;
- if (windowSessionRelayoutInfo()) {
- relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
- View.VISIBLE, 0, 0, 0, mRelayoutResult);
- } else {
- relayoutResult = mSession.relayoutLegacy(mWindow, mLayout, mWidth, mHeight,
- View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
- mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
- }
+ final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
+ View.VISIBLE, 0, 0, 0, mRelayoutResult);
final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration()
.windowConfiguration.getMaxBounds();
if (!outMaxBounds.equals(maxBounds)) {
diff --git a/core/java/android/util/SequenceUtils.java b/core/java/android/util/SequenceUtils.java
new file mode 100644
index 0000000..f833ce3
--- /dev/null
+++ b/core/java/android/util/SequenceUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Utilities to manage an info change seq id to ensure the update is in sync between client and
+ * system server. This should be used for info that can be updated though multiple IPC channel.
+ *
+ * To use it:
+ * 1. The system server should store the current seq as the source of truth, with initializing to
+ * {@link #getInitSeq}.
+ * 2. Whenever a newer info needs to be sent to the client side, the system server should first
+ * update its seq with {@link #getNextSeq}, then send the new info with the new seq to the client.
+ * 3. On the client side, when receiving a new info, it should only consume it if it is newer than
+ * the last received info seq by checking {@link #isIncomingSeqNewer}.
+ *
+ * @hide
+ */
+public final class SequenceUtils {
+
+ private SequenceUtils() {
+ }
+
+ /**
+ * Returns {@code true} if the incomingSeq is newer than the curSeq.
+ */
+ public static boolean isIncomingSeqNewer(int curSeq, int incomingSeq) {
+ // Convert to long for comparison.
+ final long diff = (long) incomingSeq - curSeq;
+ // If there has been a sufficiently large jump, assume the sequence has wrapped around.
+ // For example, when the last seq is MAX_VALUE, the incoming seq will be MIN_VALUE + 1.
+ // diff = MIN_VALUE + 1 - MAX_VALUE. It is smaller than 0, but should be treated as newer.
+ return diff > 0 || diff < Integer.MIN_VALUE;
+ }
+
+ /** Returns the initial seq. */
+ public static int getInitSeq() {
+ return Integer.MIN_VALUE;
+ }
+
+ /** Returns the next seq. */
+ public static int getNextSeq(int seq) {
+ return seq == Integer.MAX_VALUE
+ // Skip the initial seq, so that when the app process is relaunched, the incoming
+ // seq from the server is always treated as newer.
+ ? getInitSeq() + 1
+ : ++seq;
+ }
+}
diff --git a/core/java/android/view/EventLogTags.logtags b/core/java/android/view/EventLogTags.logtags
index f1cd671..f379293 100644
--- a/core/java/android/view/EventLogTags.logtags
+++ b/core/java/android/view/EventLogTags.logtags
@@ -62,6 +62,9 @@
# following other view events defined in system/logging/logcat/event.logtags
# ViewRoot Draw Events
60004 viewroot_draw_event (window|3),(event|3)
+# SurfaceView Events
+60005 surfaceview_layout (window|3),(format|1),(width|1),(height|1),(z|1),(sizeFrom|3),(attached|1),(lifecycleStrategy|1),(viewVisible|1)
+60006 surfaceview_callback (window|3),(callback|3)
# NOTE - the range 1000000-2000000 is reserved for partners and others who
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index e3e4fc0..070d33b 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -49,18 +49,6 @@
*/
interface IWindowSession {
- /**
- * Bundle key to store the latest sync seq id for the relayout configuration.
- * @see #relayout
- */
- const String KEY_RELAYOUT_BUNDLE_SEQID = "seqid";
- /**
- * Bundle key to store the latest ActivityWindowInfo associated with the relayout configuration.
- * Will only be set if the relayout window is an activity window.
- * @see #relayout
- */
- const String KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO = "activity_window_info";
-
int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, int requestedVisibleTypes,
out InputChannel outInputChannel, out InsetsState insetsState,
@@ -85,16 +73,6 @@
void remove(IBinder clientToken);
/**
- * @deprecated
- */
- int relayoutLegacy(IWindow window, in WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewVisibility,
- int flags, int seq, int lastSyncSeqId, out ClientWindowFrames outFrames,
- out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
- out InsetsState insetsState, out InsetsSourceControl.Array activeControls,
- out Bundle bundle);
-
- /**
* Change the parameters of a window. You supply the
* new parameters, it returns the new frame of the window on screen (the
* position should be ignored) and surface of the window. The surface
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 4996f5a..2302dc7 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -32,8 +32,11 @@
* registered to monitor that type of event on the targeted display.
*
* @hide
+ * @deprecated See {@link android.hardware.input.InputManager#monitorGestureInput(String, int)}
+ * for more details.
*/
@DataClass(genToString = true)
+@Deprecated
public final class InputMonitor implements Parcelable {
private static final String TAG = "InputMonitor";
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 83ff88b..1b3b3eb 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -89,7 +89,7 @@
SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
applyParams(t, surfaceParams, mTmpFloat9);
}
- t.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
t.apply();
t.close();
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 487214c..2efa647 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -18,6 +18,7 @@
import static android.graphics.PointProto.X;
import static android.graphics.PointProto.Y;
+import static android.util.SequenceUtils.getInitSeq;
import static android.view.InsetsSourceControlProto.LEASH;
import static android.view.InsetsSourceControlProto.POSITION;
import static android.view.InsetsSourceControlProto.TYPE_NUMBER;
@@ -266,6 +267,9 @@
private @Nullable InsetsSourceControl[] mControls;
+ /** To make sure the info update between client and system server is in order. */
+ private int mSeq = getInitSeq();
+
public Array() {
}
@@ -280,9 +284,18 @@
readFromParcel(in);
}
+ public int getSeq() {
+ return mSeq;
+ }
+
+ public void setSeq(int seq) {
+ mSeq = seq;
+ }
+
/** Updates the current Array to the given Array. */
public void setTo(@NonNull Array other, boolean copyControls) {
set(other.mControls, copyControls);
+ mSeq = other.mSeq;
}
/** Updates the current controls to the given controls. */
@@ -336,11 +349,13 @@
public void readFromParcel(Parcel in) {
mControls = in.createTypedArray(InsetsSourceControl.CREATOR);
+ mSeq = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeTypedArray(mControls, flags);
+ out.writeInt(mSeq);
}
public static final @NonNull Creator<Array> CREATOR = new Creator<>() {
@@ -362,6 +377,7 @@
return false;
}
final InsetsSourceControl.Array other = (InsetsSourceControl.Array) o;
+ // mSeq is for internal bookkeeping only.
return Arrays.equals(mControls, other.mControls);
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 21eec67..bbd9acf 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -17,6 +17,7 @@
package android.view;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.util.SequenceUtils.getInitSeq;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
@@ -95,6 +96,9 @@
/** The display shape */
private DisplayShape mDisplayShape = DisplayShape.NONE;
+ /** To make sure the info update between client and system server is in order. */
+ private int mSeq = getInitSeq();
+
public InsetsState() {
mSources = new SparseArray<>();
}
@@ -586,6 +590,14 @@
}
}
+ public int getSeq() {
+ return mSeq;
+ }
+
+ public void setSeq(int seq) {
+ mSeq = seq;
+ }
+
public void set(InsetsState other) {
set(other, false /* copySources */);
}
@@ -597,6 +609,7 @@
mRoundedCornerFrame.set(other.mRoundedCornerFrame);
mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
mDisplayShape = other.getDisplayShape();
+ mSeq = other.mSeq;
mSources.clear();
for (int i = 0, size = other.mSources.size(); i < size; i++) {
final InsetsSource otherSource = other.mSources.valueAt(i);
@@ -620,6 +633,7 @@
mRoundedCornerFrame.set(other.mRoundedCornerFrame);
mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
mDisplayShape = other.getDisplayShape();
+ mSeq = other.mSeq;
if (types == 0) {
return;
}
@@ -705,6 +719,7 @@
|| !mRoundedCornerFrame.equals(state.mRoundedCornerFrame)
|| !mPrivacyIndicatorBounds.equals(state.mPrivacyIndicatorBounds)
|| !mDisplayShape.equals(state.mDisplayShape)) {
+ // mSeq is for internal bookkeeping only.
return false;
}
@@ -778,6 +793,7 @@
mRoundedCornerFrame.writeToParcel(dest, flags);
dest.writeTypedObject(mPrivacyIndicatorBounds, flags);
dest.writeTypedObject(mDisplayShape, flags);
+ dest.writeInt(mSeq);
final int size = mSources.size();
dest.writeInt(size);
for (int i = 0; i < size; i++) {
@@ -803,6 +819,7 @@
mRoundedCornerFrame.readFromParcel(in);
mPrivacyIndicatorBounds = in.readTypedObject(PrivacyIndicatorBounds.CREATOR);
mDisplayShape = in.readTypedObject(DisplayShape.CREATOR);
+ mSeq = in.readInt();
final int size = in.readInt();
final SparseArray<InsetsSource> sources;
if (mSources == null) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 79a9f2d..72d2d3b 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,15 +16,6 @@
package android.view;
-import static android.os.IInputConstants.INPUT_EVENT_FLAG_CANCELED;
-import static android.os.IInputConstants.MOTION_EVENT_FLAG_HOVER_EXIT_PENDING;
-import static android.os.IInputConstants.INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
-import static android.os.IInputConstants.MOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
-import static android.os.IInputConstants.MOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
-import static android.os.IInputConstants.INPUT_EVENT_FLAG_TAINTED;
-import static android.os.IInputConstants.MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS;
-import static android.os.IInputConstants.MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
-import static android.os.IInputConstants.MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
import static android.view.Display.DEFAULT_DISPLAY;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -38,6 +29,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
import android.os.Build;
+import android.os.MotionEventFlag;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -476,7 +468,7 @@
* to drop the suspect touches or to take additional precautions to confirm the user's
* actual intent.
*/
- public static final int FLAG_WINDOW_IS_OBSCURED = MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ public static final int FLAG_WINDOW_IS_OBSCURED = MotionEventFlag.WINDOW_IS_OBSCURED;
/**
* This flag indicates that the window that received this motion event is partly
@@ -493,7 +485,7 @@
* obstructed in areas other than the touched location.
*/
public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED =
- MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ MotionEventFlag.WINDOW_IS_PARTIALLY_OBSCURED;
/**
* This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that
@@ -501,7 +493,7 @@
* prevent generating redundant {@link #ACTION_HOVER_ENTER} events.
* @hide
*/
- public static final int FLAG_HOVER_EXIT_PENDING = MOTION_EVENT_FLAG_HOVER_EXIT_PENDING;
+ public static final int FLAG_HOVER_EXIT_PENDING = MotionEventFlag.HOVER_EXIT_PENDING;
/**
* This flag indicates that the event has been generated by a gesture generator. It
@@ -509,7 +501,7 @@
*
* @hide
*/
- public static final int FLAG_IS_GENERATED_GESTURE = MOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+ public static final int FLAG_IS_GENERATED_GESTURE = MotionEventFlag.IS_GENERATED_GESTURE;
/**
* This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
@@ -522,7 +514,7 @@
* @see #ACTION_POINTER_UP
* @see #ACTION_CANCEL
*/
- public static final int FLAG_CANCELED = INPUT_EVENT_FLAG_CANCELED;
+ public static final int FLAG_CANCELED = MotionEventFlag.CANCELED;
/**
* This flag indicates that the event will not cause a focus change if it is directed to an
@@ -531,7 +523,7 @@
* window into focus.
* @hide
*/
- public static final int FLAG_NO_FOCUS_CHANGE = MOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ public static final int FLAG_NO_FOCUS_CHANGE = MotionEventFlag.NO_FOCUS_CHANGE;
/**
* This flag indicates that this event was modified by or generated from an accessibility
@@ -539,7 +531,7 @@
* @hide
*/
@TestApi
- public static final int FLAG_IS_ACCESSIBILITY_EVENT = INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
+ public static final int FLAG_IS_ACCESSIBILITY_EVENT = MotionEventFlag.IS_ACCESSIBILITY_EVENT;
/**
* Private flag that indicates when the system has detected that this motion event
@@ -550,7 +542,7 @@
* @see #isTainted
* @see #setTainted
*/
- public static final int FLAG_TAINTED = INPUT_EVENT_FLAG_TAINTED;
+ public static final int FLAG_TAINTED = MotionEventFlag.TAINTED;
/**
* Private flag indicating that this event was synthesized by the system and should be delivered
@@ -566,7 +558,7 @@
* @see #setTargetAccessibilityFocus(boolean)
*/
public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS =
- MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS;
+ MotionEventFlag.TARGET_ACCESSIBILITY_FOCUS;
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index e20b15e..396be7b 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -422,7 +422,7 @@
/**
* Free all server-side state associated with this surface and
* release this object's reference. This method can only be
- * called from the process that created the service.
+ * called from the process that created the surface.
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0bdb4ad..f653524 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2812,6 +2812,10 @@
private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
private final ArrayMap<SurfaceControl, SurfaceControl> mReparentedSurfaces =
new ArrayMap<>();
+ // Only non-null if the SurfaceControlRegistry is enabled. This list tracks the set of calls
+ // made through this transaction object, and is dumped (and cleared) when the transaction is
+ // later applied.
+ ArrayList<String> mCalls;
Runnable mFreeNativeResources;
private static final float[] INVALID_COLOR = {-1, -1, -1};
@@ -2837,13 +2841,28 @@
private Transaction(long nativeObject) {
mNativeObject = nativeObject;
mFreeNativeResources = sRegistry.registerNativeAllocation(this, mNativeObject);
- if (!SurfaceControlRegistry.sCallStackDebuggingInitialized) {
- SurfaceControlRegistry.initializeCallStackDebugging();
- }
+ setUpForSurfaceControlRegistry();
}
private Transaction(Parcel in) {
readFromParcel(in);
+ setUpForSurfaceControlRegistry();
+ }
+
+ /**
+ * Sets up this transaction for the SurfaceControlRegistry.
+ */
+ private void setUpForSurfaceControlRegistry() {
+ if (!SurfaceControlRegistry.sCallStackDebuggingInitialized) {
+ SurfaceControlRegistry.initializeCallStackDebugging();
+ }
+ mCalls = SurfaceControlRegistry.sLogAllTxCallsOnApply
+ ? new ArrayList<>()
+ : null;
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "ctor", this, null, null);
+ }
}
/**
@@ -2893,6 +2912,9 @@
if (mNativeObject != 0) {
nativeClearTransaction(mNativeObject);
}
+ if (mCalls != null) {
+ mCalls.clear();
+ }
}
/**
@@ -2904,6 +2926,9 @@
mReparentedSurfaces.clear();
mFreeNativeResources.run();
mNativeObject = 0;
+ if (mCalls != null) {
+ mCalls.clear();
+ }
}
/**
@@ -2921,7 +2946,10 @@
if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
- "apply", this, null, null);
+ SurfaceControlRegistry.APPLY, this, null, null);
+ }
+ if (mCalls != null) {
+ mCalls.clear();
}
}
@@ -4421,6 +4449,14 @@
if (this == other) {
return this;
}
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "merge", this, null, "otherTx=" + other.getId());
+ if (mCalls != null) {
+ mCalls.addAll(other.mCalls);
+ other.mCalls.clear();
+ }
+ }
mResizedSurfaces.putAll(other.mResizedSurfaces);
other.mResizedSurfaces.clear();
mReparentedSurfaces.putAll(other.mReparentedSurfaces);
@@ -4472,6 +4508,10 @@
Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled");
return this;
}
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setFrameTimeline", this, null, "vsyncId=" + vsyncId);
+ }
nativeSetFrameTimelineVsync(mNativeObject, vsyncId);
return this;
}
@@ -4479,6 +4519,11 @@
/** @hide */
@NonNull
public Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
+ if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+ SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+ "setFrameTimelineVsync", this, null, "frameTimelineVsyncId="
+ + frameTimelineVsyncId);
+ }
nativeSetFrameTimelineVsync(mNativeObject, frameTimelineVsyncId);
return this;
}
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index aa3654d..117b200 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -44,6 +44,8 @@
*/
public class SurfaceControlRegistry {
private static final String TAG = "SurfaceControlRegistry";
+ // Special constant for identifying the Transaction#apply() calls
+ static final String APPLY = "apply";
/**
* An interface for processing the registered SurfaceControls when the threshold is exceeded.
@@ -134,6 +136,10 @@
// sCallStackDebuggingEnabled is true. Can be combined with the match name.
private static String sCallStackDebuggingMatchCall;
+ // When set, all calls on a SurfaceControl.Transaction will be stored and logged when the
+ // transaction is applied.
+ static boolean sLogAllTxCallsOnApply;
+
// Mapping of the active SurfaceControls to the elapsed time when they were registered
@GuardedBy("sLock")
private final WeakHashMap<SurfaceControl, Long> mSurfaceControls;
@@ -185,6 +191,7 @@
public void setCallStackDebuggingParams(String matchName, String matchCall) {
sCallStackDebuggingMatchName = matchName.toLowerCase();
sCallStackDebuggingMatchCall = matchCall.toLowerCase();
+ sLogAllTxCallsOnApply = sCallStackDebuggingMatchCall.contains("apply");
}
/**
@@ -294,13 +301,15 @@
sCallStackDebuggingMatchName =
SystemProperties.get("persist.wm.debug.sc.tx.log_match_name", null)
.toLowerCase();
+ sLogAllTxCallsOnApply = sCallStackDebuggingMatchCall.contains("apply");
// Only enable stack debugging if any of the match filters are set
- sCallStackDebuggingEnabled = (!sCallStackDebuggingMatchCall.isEmpty()
- || !sCallStackDebuggingMatchName.isEmpty());
+ sCallStackDebuggingEnabled = !sCallStackDebuggingMatchCall.isEmpty()
+ || !sCallStackDebuggingMatchName.isEmpty();
if (sCallStackDebuggingEnabled) {
Log.d(TAG, "Enabling transaction call stack debugging:"
+ " matchCall=" + sCallStackDebuggingMatchCall
- + " matchName=" + sCallStackDebuggingMatchName);
+ + " matchName=" + sCallStackDebuggingMatchName
+ + " logCallsWithApply=" + sLogAllTxCallsOnApply);
}
}
@@ -319,15 +328,31 @@
if (!sCallStackDebuggingEnabled) {
return;
}
- if (!matchesForCallStackDebugging(sc != null ? sc.getName() : null, call)) {
- return;
- }
- final String txMsg = tx != null ? "tx=" + tx.getId() + " ": "";
- final String scMsg = sc != null ? " sc=" + sc.getName() + "": "";
+
+ final String txMsg = tx != null ? "tx=" + tx.getId() + " " : "";
+ final String scMsg = sc != null ? " sc=" + sc.getName() + "" : "";
final String msg = details != null
? call + " (" + txMsg + scMsg + ") " + details
: call + " (" + txMsg + scMsg + ")";
- Log.e(TAG, msg, new Throwable());
+ if (sLogAllTxCallsOnApply && tx != null) {
+ if (call == APPLY) {
+ // Log the apply and dump the calls on that transaction
+ Log.e(TAG, msg, new Throwable());
+ for (int i = 0; i < tx.mCalls.size(); i++) {
+ Log.d(TAG, " " + tx.mCalls.get(i));
+ }
+ } else if (matchesForCallStackDebugging(sc != null ? sc.getName() : null, call)) {
+ // Otherwise log this call to the transaction if it matches the tracked calls
+ Log.e(TAG, msg, new Throwable());
+ tx.mCalls.add(msg);
+ }
+ } else {
+ // Log this call if it matches the tracked calls
+ if (!matchesForCallStackDebugging(sc != null ? sc.getName() : null, call)) {
+ return;
+ }
+ Log.e(TAG, msg, new Throwable());
+ }
}
/**
@@ -388,6 +413,7 @@
pw.println("sCallStackDebuggingEnabled=" + sCallStackDebuggingEnabled);
pw.println("sCallStackDebuggingMatchName=" + sCallStackDebuggingMatchName);
pw.println("sCallStackDebuggingMatchCall=" + sCallStackDebuggingMatchCall);
+ pw.println("sLogAllTxCallsOnApply=" + sLogAllTxCallsOnApply);
}
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1d70d18..fedbe4a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -49,6 +49,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.EventLog;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -163,6 +164,8 @@
private static final boolean DEBUG_POSITION = false;
private static final long FORWARD_BACK_KEY_TOLERANCE_MS = 100;
+ private static final int LOGTAG_SURFACEVIEW_LAYOUT = 60005;
+ private static final int LOGTAG_SURFACEVIEW_CALLBACK = 60006;
@UnsupportedAppUsage(
maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
@@ -320,6 +323,8 @@
private final ConcurrentLinkedQueue<WindowManager.LayoutParams> mEmbeddedWindowParams =
new ConcurrentLinkedQueue<>();
+ private String mTag = TAG;
+
private final ISurfaceControlViewHostParent mSurfaceControlViewHostParent =
new ISurfaceControlViewHostParent.Stub() {
@Override
@@ -418,13 +423,26 @@
updateSurface();
}
+ private void setTag() {
+ String windowName = "";
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ // strip package name
+ final String[] split = viewRoot.mWindowAttributes.getTitle().toString().split("\\.");
+ if (split.length > 0) {
+ windowName = " " + split[split.length - 1];
+ }
+ }
+
+ mTag = "SV[" + System.identityHashCode(this) + windowName + "]";
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
-
+ setTag();
getViewRootImpl().addSurfaceChangedCallback(this);
mWindowStopped = false;
-
mViewVisibility = getVisibility() == VISIBLE;
updateRequestedVisibility();
@@ -1146,13 +1164,22 @@
+ " format=" + formatChanged + " size=" + sizeChanged
+ " visible=" + visibleChanged + " alpha=" + alphaChanged
+ " hint=" + hintChanged
- + " visible=" + visibleChanged
+ " left=" + (mWindowSpaceLeft != mLocation[0])
+ " top=" + (mWindowSpaceTop != mLocation[1])
+ " z=" + relativeZChanged
+ " attached=" + mAttachedToWindow
+ " lifecycleStrategy=" + surfaceLifecycleStrategyChanged);
+ if (creating || formatChanged || sizeChanged || visibleChanged
+ || layoutSizeChanged || relativeZChanged || !mAttachedToWindow
+ || surfaceLifecycleStrategyChanged ) {
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_LAYOUT,
+ mTag, mRequestedFormat, myWidth, myHeight, mRequestedSubLayer,
+ (mRequestedWidth > 0 ? "setFixedSize" : "layout"),
+ (mAttachedToWindow ? 1 : 0),
+ mRequestedSurfaceLifecycleStrategy, (mRequestedVisible ? 1 : 0));
+ }
+
try {
mVisible = mRequestedVisible;
mWindowSpaceLeft = mLocation[0];
@@ -1235,6 +1262,8 @@
mIsCreating = true;
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "visibleChanged -- surfaceCreated");
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceCreated");
callbacks = getSurfaceCallbacks();
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
@@ -1245,6 +1274,9 @@
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "surfaceChanged -- format=" + mFormat
+ " w=" + myWidth + " h=" + myHeight);
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceChanged -- format=" + mFormat
+ + " w=" + myWidth + " h=" + myHeight);
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
@@ -1256,6 +1288,9 @@
if (DEBUG) {
Log.i(TAG, System.identityHashCode(this) + " surfaceRedrawNeeded");
}
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceRedrawNeeded");
+
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
@@ -1337,7 +1372,7 @@
private void redrawNeededAsync(SurfaceHolder.Callback[] callbacks,
Runnable callbacksCollected) {
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(callbacksCollected);
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(callbacksCollected, mTag);
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
}
@@ -2100,6 +2135,7 @@
if (mSurface.isValid()) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "surfaceDestroyed");
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag, "surfaceDestroyed");
SurfaceHolder.Callback[] callbacks = getSurfaceCallbacks();
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9bc1511..4766942 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -27534,7 +27534,7 @@
* @return a view with given ID if found, or {@code null} otherwise
* @see View#requireViewById(int)
*/
- @Nullable
+ /* TODO(b/347672184): Re-add @Nullable */
public final <T extends View> T findViewById(@IdRes int id) {
if (id == NO_ID) {
return null;
@@ -30607,7 +30607,8 @@
* {@link #setPointerIcon(PointerIcon)} for mouse devices. Subclasses may override this to
* customize the icon for the given pointer.
*
- * For example, the pointer icon for a stylus pointer can be resolved in the following way:
+ * For example, to always show the PointerIcon.TYPE_HANDWRITING icon for a stylus pointer,
+ * the event can be resolved in the following way:
* <code><pre>
* @Override
* public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
@@ -30617,7 +30618,7 @@
* && (toolType == MotionEvent.TOOL_TYPE_STYLUS
* || toolType == MotionEvent.TOOL_TYPE_ERASER)) {
* // Show this pointer icon only if this pointer is a stylus.
- * return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_WAIT);
+ * return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HANDWRITING);
* }
* // Use the default logic for determining the pointer icon for other non-stylus pointers,
* // like for the mouse cursor.
@@ -33898,8 +33899,19 @@
protected int calculateFrameRateCategory() {
int category;
switch (getViewRootImpl().intermittentUpdateState()) {
- case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT -> category =
- FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
+ case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT -> {
+ if (!sToolkitFrameRateBySizeReadOnlyFlagValue) {
+ category = FRAME_RATE_CATEGORY_NORMAL;
+ } else {
+ // The size based frame rate category can only be LOW or NORMAL. If the size
+ // based frame rate category is LOW, we shouldn't vote for NORMAL for
+ // intermittent.
+ category = Math.min(
+ mSizeBasedFrameRateCategoryAndReason & ~FRAME_RATE_CATEGORY_REASON_MASK,
+ FRAME_RATE_CATEGORY_NORMAL);
+ }
+ category |= FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
+ }
case ViewRootImpl.INTERMITTENT_STATE_NOT_INTERMITTENT ->
category = mSizeBasedFrameRateCategoryAndReason;
default -> category = mLastFrameRateCategory;
@@ -33921,6 +33933,11 @@
float velocity = mFrameContentVelocity;
final float frameRate = mPreferredFrameRate;
ViewParent parent = mParent;
+ boolean isInputMethodWindowType = false;
+ if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) {
+ isInputMethodWindowType =
+ mAttachInfo.mViewRootImpl.mWindowAttributes.type == TYPE_INPUT_METHOD;
+ }
if (velocity <= 0 && Float.isNaN(frameRate)) {
// The most common case is when nothing is set, so this special case is called
// often.
@@ -33930,7 +33947,8 @@
|| mLastFrameTop != mTop)
&& viewRootImpl.shouldCheckFrameRate(false)
&& parent instanceof View
- && ((View) parent).mFrameContentVelocity <= 0) {
+ && ((View) parent).mFrameContentVelocity <= 0
+ && !isInputMethodWindowType) {
viewRootImpl.votePreferredFrameRate(MAX_FRAME_RATE, FRAME_RATE_COMPATIBILITY_GTE);
}
if (viewRootImpl.shouldCheckFrameRateCategory()) {
@@ -33953,6 +33971,7 @@
|| mLastFrameTop != mTop)
&& mParent instanceof View
&& ((View) mParent).mFrameContentVelocity <= 0
+ && !isInputMethodWindowType
) {
// This current calculation is very simple. If something on the screen
// moved, then it votes for the highest velocity.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ac59b2a..c0bd535 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -22,11 +22,11 @@
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
import static android.os.Trace.TRACE_TAG_VIEW;
+import static android.util.SequenceUtils.getInitSeq;
+import static android.util.SequenceUtils.isIncomingSeqNewer;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.DragEvent.ACTION_DRAG_LOCATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
-import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
@@ -90,6 +90,7 @@
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -114,6 +115,7 @@
import static android.view.accessibility.Flags.reduceWindowContentChangedEventThrottle;
import static android.view.flags.Flags.addSchandleToVriSurface;
import static android.view.flags.Flags.sensitiveContentAppProtection;
+import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix;
import static android.view.flags.Flags.toolkitFrameRateFunctionEnablingReadOnly;
import static android.view.flags.Flags.toolkitFrameRateTypingReadOnly;
import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly;
@@ -124,11 +126,12 @@
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
import static com.android.window.flags.Flags.activityWindowInfoFlag;
import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
+import static com.android.window.flags.Flags.insetsControlChangedItem;
+import static com.android.window.flags.Flags.insetsControlSeq;
import static com.android.window.flags.Flags.setScPropertiesInClient;
-import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
-import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -892,6 +895,12 @@
/** Non-{@code null} if {@link #mActivityConfigCallback} is not {@code null}. */
@Nullable
private ActivityWindowInfo mLastReportedActivityWindowInfo;
+ @Nullable
+ private final ClientWindowFrames mLastReportedFrames = insetsControlSeq()
+ ? new ClientWindowFrames()
+ : null;
+ private int mLastReportedInsetsStateSeq = getInitSeq();
+ private int mLastReportedActiveControlsSeq = getInitSeq();
boolean mScrollMayChange;
@SoftInputModeFlags
@@ -1107,6 +1116,8 @@
private boolean mIsFrameRateBoosting = false;
// Used to check if it is in touch boosting period.
private boolean mIsTouchBoosting = false;
+ // Used to track if an ongoing press gesture is occurring.
+ private boolean mIsPressedGesture = false;
private boolean mDrawnThisFrame = false;
// Used to check if there is a conflict between different frame rate voting.
// Take 24 and 30 as an example, 24 is not a divisor of 30.
@@ -1137,7 +1148,7 @@
*/
/**
- * A temporary object used so relayoutWindow can return the latest SyncSeqId
+ * Object for relayoutWindow to return the latest window info, including the SyncSeqId
* system. The SyncSeqId system was designed to work without synchronous relayout
* window, and actually synchronous relayout window presents a problem. We could have
* a sequence like this:
@@ -1151,14 +1162,8 @@
* we get rid of synchronous relayout, until then, we use this bundle to channel the
* integer back over relayout.
*/
- private final Bundle mRelayoutBundle = windowSessionRelayoutInfo()
- ? null
- : new Bundle();
-
- private final WindowRelayoutResult mRelayoutResult = windowSessionRelayoutInfo()
- ? new WindowRelayoutResult(mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
- mTempInsets, mTempControls)
- : null;
+ private final WindowRelayoutResult mRelayoutResult = new WindowRelayoutResult(
+ mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls);
private static volatile boolean sAnrReported = false;
static BLASTBufferQueue.TransactionHangCallback sTransactionHangCallback =
@@ -1266,7 +1271,6 @@
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
- // TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
mChoreographer = Choreographer.getInstance();
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
mImeBackAnimationController = new ImeBackAnimationController(this, mInsetsController);
@@ -1601,8 +1605,6 @@
attachedFrame = null;
}
if (mTranslator != null) {
- mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
- mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls.get());
mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
mTmpFrames.attachedFrame = attachedFrame;
@@ -1625,8 +1627,7 @@
mAttachInfo.mAlwaysConsumeSystemBars =
(res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
- mInsetsController.onStateChanged(mTempInsets);
- mInsetsController.onControlsChanged(mTempControls.get());
+ handleInsetsControlChanged(mTempInsets, mTempControls);
final InsetsState state = mInsetsController.getState();
final Rect displayCutoutSafe = mTempRect;
state.getDisplayCutoutSafe(displayCutoutSafe);
@@ -2224,17 +2225,18 @@
return;
}
+ onClientWindowFramesChanged(frames);
+
CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
final Rect frame = frames.frame;
final Rect displayFrame = frames.displayFrame;
final Rect attachedFrame = frames.attachedFrame;
if (mTranslator != null) {
- mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
mTranslator.translateRectInScreenToAppWindow(frame);
mTranslator.translateRectInScreenToAppWindow(displayFrame);
mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
- mInsetsController.onStateChanged(insetsState);
+ onInsetsStateChanged(insetsState);
final float compatScale = frames.compatScale;
final boolean frameChanged = !mWinFrame.equals(frame);
final boolean shouldReportActivityWindowInfoChanged =
@@ -2299,26 +2301,69 @@
}
/** Handles messages {@link #MSG_INSETS_CONTROL_CHANGED}. */
- private void handleInsetsControlChanged(@NonNull InsetsState insetsState,
+ @VisibleForTesting
+ public void handleInsetsControlChanged(@NonNull InsetsState insetsState,
@NonNull InsetsSourceControl.Array activeControls) {
- final InsetsSourceControl[] controls = activeControls.get();
-
- if (mTranslator != null) {
- mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
- mTranslator.translateSourceControlsInScreenToAppWindow(controls);
- }
-
// Deliver state change before control change, such that:
// a) When gaining control, controller can compare with server state to evaluate
// whether it needs to run animation.
// b) When loosing control, controller can restore server state by taking last
// dispatched state as truth.
- mInsetsController.onStateChanged(insetsState);
- if (mAdded) {
- mInsetsController.onControlsChanged(controls);
- } else {
- activeControls.release();
+ onInsetsStateChanged(insetsState);
+ onActiveControlsChanged(activeControls);
+ }
+
+ private void onClientWindowFramesChanged(@NonNull ClientWindowFrames inOutFrames) {
+ if (mLastReportedFrames == null) {
+ return;
}
+ if (isIncomingSeqNewer(mLastReportedFrames.seq, inOutFrames.seq)) {
+ // Keep track of the latest.
+ mLastReportedFrames.setTo(inOutFrames);
+ } else {
+ // If the last reported frames is newer, use the last reported instead.
+ inOutFrames.setTo(mLastReportedFrames);
+ }
+ }
+
+ private void onInsetsStateChanged(@NonNull InsetsState insetsState) {
+ if (insetsControlSeq()) {
+ if (isIncomingSeqNewer(mLastReportedInsetsStateSeq, insetsState.getSeq())) {
+ mLastReportedInsetsStateSeq = insetsState.getSeq();
+ } else {
+ // The last reported InsetsState is newer. Skip.
+ return;
+ }
+ }
+
+ if (mTranslator != null) {
+ mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
+ }
+ mInsetsController.onStateChanged(insetsState);
+ }
+
+ private void onActiveControlsChanged(@NonNull InsetsSourceControl.Array activeControls) {
+ if (!mAdded) {
+ // Do not update the last report if window is not added yet.
+ activeControls.release();
+ return;
+ }
+
+ if (insetsControlSeq()) {
+ if (isIncomingSeqNewer(mLastReportedActiveControlsSeq, activeControls.getSeq())) {
+ mLastReportedActiveControlsSeq = activeControls.getSeq();
+ } else {
+ // The last reported controls is newer. Skip.
+ activeControls.release();
+ return;
+ }
+ }
+
+ final InsetsSourceControl[] controls = activeControls.get();
+ if (mTranslator != null) {
+ mTranslator.translateSourceControlsInScreenToAppWindow(controls);
+ }
+ mInsetsController.onControlsChanged(controls);
}
private final DisplayListener mDisplayListener = new DisplayListener() {
@@ -4405,7 +4450,7 @@
if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) {
mAppStartTrackingStarted = true;
Transaction transaction = new Transaction();
- transaction.addTransactionCompletedListener(mExecutor,
+ transaction.addTransactionCompletedListener(mSimpleExecutor,
new Consumer<TransactionStats>() {
@Override
public void accept(TransactionStats transactionStats) {
@@ -6919,6 +6964,7 @@
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
mInvalidationIdleMessagePosted = false;
+ mIsPressedGesture = false;
} else {
mInvalidationIdleMessagePosted = true;
mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
@@ -7932,6 +7978,9 @@
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
final int action = event.getAction();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mIsPressedGesture = false;
+ }
boolean handled = false;
if (!disableHandwritingInitiatorForIme()
|| mWindowAttributes.type != TYPE_INPUT_METHOD) {
@@ -7962,6 +8011,9 @@
mWindowAttributes.type)) {
// set the frame rate to the maximum value.
mIsTouchBoosting = true;
+ if (action == MotionEvent.ACTION_DOWN) {
+ mIsPressedGesture = true;
+ }
setPreferredFrameRateCategory(mLastPreferredFrameRateCategory);
}
/**
@@ -9260,42 +9312,21 @@
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mRelayoutSeq,
mLastSyncSeqId);
} else {
- if (windowSessionRelayoutInfo()) {
- relayoutResult = mWindowSession.relayout(mWindow, params,
- requestedWidth, requestedHeight, viewVisibility,
- insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
- mRelayoutSeq, mLastSyncSeqId, mRelayoutResult);
- } else {
- relayoutResult = mWindowSession.relayoutLegacy(mWindow, params,
- requestedWidth, requestedHeight, viewVisibility,
- insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
- mRelayoutSeq, mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration,
- mSurfaceControl, mTempInsets, mTempControls, mRelayoutBundle);
- }
+ relayoutResult = mWindowSession.relayout(mWindow, params,
+ requestedWidth, requestedHeight, viewVisibility,
+ insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+ mRelayoutSeq, mLastSyncSeqId, mRelayoutResult);
mRelayoutRequested = true;
+ onClientWindowFramesChanged(mTmpFrames);
+
if (activityWindowInfoFlag() && mPendingActivityWindowInfo != null) {
- ActivityWindowInfo outInfo = null;
- if (windowSessionRelayoutInfo()) {
- outInfo = mRelayoutResult != null ? mRelayoutResult.activityWindowInfo : null;
- } else {
- try {
- outInfo = mRelayoutBundle.getParcelable(
- IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO,
- ActivityWindowInfo.class);
- mRelayoutBundle.remove(
- IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO);
- } catch (IllegalStateException e) {
- Log.e(TAG, "Failed to get ActivityWindowInfo from relayout Bundle", e);
- }
- }
+ final ActivityWindowInfo outInfo = mRelayoutResult.activityWindowInfo;
if (outInfo != null) {
mPendingActivityWindowInfo.set(outInfo);
}
}
- final int maybeSyncSeqId = windowSessionRelayoutInfo()
- ? mRelayoutResult.syncSeqId
- : mRelayoutBundle.getInt(IWindowSession.KEY_RELAYOUT_BUNDLE_SEQID);
+ final int maybeSyncSeqId = mRelayoutResult.syncSeqId;
if (maybeSyncSeqId > 0) {
mSyncSeqId = maybeSyncSeqId;
}
@@ -9305,13 +9336,10 @@
mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame);
- mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
- mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls.get());
}
mInvCompatScale = 1f / mTmpFrames.compatScale;
CompatibilityInfo.applyOverrideScaleIfNeeded(mPendingMergedConfiguration);
- mInsetsController.onStateChanged(mTempInsets);
- mInsetsController.onControlsChanged(mTempControls.get());
+ handleInsetsControlChanged(mTempInsets, mTempControls);
mPendingAlwaysConsumeSystemBars =
(relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
@@ -11429,8 +11457,13 @@
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl.Array activeControls) {
- final boolean isFromInsetsControlChangeItem = mIsFromTransactionItem;
- mIsFromTransactionItem = false;
+ final boolean isFromInsetsControlChangeItem;
+ if (insetsControlChangedItem()) {
+ isFromInsetsControlChangeItem = mIsFromTransactionItem;
+ mIsFromTransactionItem = false;
+ } else {
+ isFromInsetsControlChangeItem = false;
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor == null) {
if (isFromInsetsControlChangeItem) {
@@ -13082,7 +13115,7 @@
if (frameRate <= 0) {
return;
}
- if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) {
+ if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && !mIsPressedGesture) {
mIsTouchBoosting = false;
mIsFrameRateBoosting = false;
if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 52000d9..7cb70bd 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1722,7 +1722,7 @@
* @see View#findViewById(int)
* @see Window#requireViewById(int)
*/
- @Nullable
+ /* TODO(b/347672184): Re-add @Nullable */
public <T extends View> T findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ae051f9..18006bb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -6387,10 +6387,6 @@
* The host is likely to be an {@link AttachedSurfaceControl} so the host token can be
* retrieved via {@link AttachedSurfaceControl#getInputTransferToken()}.
* <p><br>
- * Only the window currently receiving touch is allowed to transfer the gesture so if the caller
- * attempts to transfer touch gesture from a token that doesn't have touch, it will fail the
- * transfer.
- * <p><br>
* When the host wants to transfer touch gesture to the embedded, it can retrieve the embedded
* token via {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()} or use the
* value returned from either
@@ -6414,6 +6410,23 @@
* arrives, input dispatcher will do a new round of hit testing. So, if the host window is
* still the first thing that's being touched, then it will receive the new gesture again. It
* will again be up to the host to transfer this new gesture to the embedded.
+ * <p><br>
+ * The call can fail for the following reasons:
+ * <ul>
+ * <li>
+ * Caller attempts to transfer touch gesture from a token that doesn't have an active gesture.
+ * </li>
+ * <li>
+ * The gesture is transferred to a token that is not associated with the transferFromToken. For
+ * example, if the caller transfers to a {@link SurfaceControlViewHost} not attached to the
+ * host window via {@link SurfaceView#setChildSurfacePackage(SurfacePackage)}.
+ * </li>
+ * <li>
+ * The active gesture completes before the transfer is complete, such as in the case of a
+ * fling.
+ * </li>
+ * </ul>
+ * <p>
*
* @param transferFromToken the InputTransferToken for the currently active gesture
* @param transferToToken the InputTransferToken to transfer the gesture to.
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index d7d764b..55f22a6 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -23,7 +23,6 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -349,18 +348,6 @@
}
@Override
- public int relayoutLegacy(IWindow window, WindowManager.LayoutParams inAttrs,
- int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
- int lastSyncSeqId, ClientWindowFrames outFrames,
- MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
- InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
- Bundle outSyncSeqIdBundle) {
- return relayoutInner(window, inAttrs, requestedWidth, requestedHeight, viewFlags, flags,
- seq, lastSyncSeqId, outFrames, outMergedConfiguration, outSurfaceControl,
- outInsetsState, outActiveControls);
- }
-
- @Override
public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
int lastSyncSeqId, WindowRelayoutResult outRelayoutResult) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 3fc9ebc..a4cea33 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -19,6 +19,8 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -62,6 +64,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Display;
import android.view.IWindow;
import android.view.SurfaceControl;
import android.view.View;
@@ -69,6 +72,7 @@
import com.android.internal.R;
import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IntPair;
@@ -1565,7 +1569,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
public void performAccessibilityShortcut() {
- performAccessibilityShortcut(null);
+ performAccessibilityShortcut(Display.DEFAULT_DISPLAY, HARDWARE, null);
}
/**
@@ -1577,7 +1581,8 @@
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
- public void performAccessibilityShortcut(@Nullable String targetName) {
+ public void performAccessibilityShortcut(
+ int displayId, @UserShortcutType int shortcutType, @Nullable String targetName) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -1586,7 +1591,7 @@
}
}
try {
- service.performAccessibilityShortcut(targetName);
+ service.performAccessibilityShortcut(displayId, shortcutType, targetName);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
}
@@ -1602,7 +1607,7 @@
*/
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
public void enableShortcutsForTargets(boolean enable,
- @ShortcutConstants.UserShortcutType int shortcutTypes, @NonNull Set<String> targets,
+ @UserShortcutType int shortcutTypes, @NonNull Set<String> targets,
@UserIdInt int userId) {
final IAccessibilityManager service;
synchronized (mLock) {
@@ -1817,7 +1822,7 @@
@RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
@NonNull
public List<String> getAccessibilityShortcutTargets(
- @ShortcutConstants.UserShortcutType int shortcutType) {
+ @UserShortcutType int shortcutType) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index cd11314..72a1fe4 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -92,7 +92,7 @@
void notifyAccessibilityButtonVisibilityChanged(boolean available);
@EnforcePermission("MANAGE_ACCESSIBILITY")
- void performAccessibilityShortcut(String targetName);
+ void performAccessibilityShortcut(int displayId, int shortcutType, String targetName);
@EnforcePermission("MANAGE_ACCESSIBILITY")
List<String> getAccessibilityShortcutTargets(int shortcutType);
diff --git a/core/java/android/view/accessibility/a11ychecker/AccessibilityNodePathBuilder.java b/core/java/android/view/accessibility/a11ychecker/AccessibilityNodePathBuilder.java
new file mode 100644
index 0000000..2996dde
--- /dev/null
+++ b/core/java/android/view/accessibility/a11ychecker/AccessibilityNodePathBuilder.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility.a11ychecker;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * Utility class to create developer-friendly {@link AccessibilityNodeInfo} path Strings for use
+ * in reporting AccessibilityCheck results.
+ *
+ * @hide
+ */
+public final class AccessibilityNodePathBuilder {
+
+ /**
+ * Returns the path of the node within its accessibility hierarchy starting from the root node
+ * down to the given node itself, and prefixed by the package name. This path is not guaranteed
+ * to be unique. This can return null in case the node's hierarchy changes while scanning.
+ *
+ * <p>Each element in the path is represented by its View ID resource name, when available, or
+ * the
+ * simple class name if not. The path also includes the index of each child node relative to
+ * its
+ * parent. See {@link AccessibilityNodeInfo#getViewIdResourceName()}.
+ *
+ * <p>For example,
+ * "com.example.app:RootElementClassName/parent_resource_name[1]/TargetElementClassName[3]"
+ * indicates the element has type {@code TargetElementClassName}, and is the third child of an
+ * element with the resource name {@code parent_resource_name}, which is the first child of an
+ * element of type {@code RootElementClassName}.
+ *
+ * <p>This format is consistent with elements paths in Pre-Launch Reports and the Accessibility
+ * Scanner, starting from the window's root node instead of the first resource name.
+ * TODO (b/344607035): link to ClusteringUtils when AATF is merged in main.
+ */
+ public static @Nullable String createNodePath(@NonNull AccessibilityNodeInfo nodeInfo) {
+ StringBuilder resourceIdBuilder = getNodePathBuilder(nodeInfo);
+ return resourceIdBuilder == null ? null : String.valueOf(nodeInfo.getPackageName()) + ':'
+ + resourceIdBuilder;
+ }
+
+ private static @Nullable StringBuilder getNodePathBuilder(AccessibilityNodeInfo nodeInfo) {
+ AccessibilityNodeInfo parent = nodeInfo.getParent();
+ if (parent == null) {
+ return new StringBuilder(getShortUiElementName(nodeInfo));
+ }
+ StringBuilder parentNodePath = getNodePathBuilder(parent);
+ if (parentNodePath == null) {
+ return null;
+ }
+ int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (!nodeInfo.equals(parent.getChild(i))) {
+ continue;
+ }
+ CharSequence uiElementName = getShortUiElementName(nodeInfo);
+ if (uiElementName != null) {
+ parentNodePath.append('/').append(uiElementName).append('[').append(i + 1).append(
+ ']');
+ } else {
+ parentNodePath.append(":nth-child(").append(i + 1).append(')');
+ }
+ return parentNodePath;
+ }
+ return null;
+ }
+
+ //Returns the part of the element's View ID resource name after the qualifier
+ // "package_name:id/" or the last '/', when available. Otherwise, returns the element's
+ // simple class name.
+ private static CharSequence getShortUiElementName(AccessibilityNodeInfo nodeInfo) {
+ String viewIdResourceName = nodeInfo.getViewIdResourceName();
+ if (viewIdResourceName != null) {
+ String idQualifier = ":id/";
+ int idQualifierStartIndex = viewIdResourceName.indexOf(idQualifier);
+ int unqualifiedNameStartIndex = idQualifierStartIndex == -1 ? 0
+ : (idQualifierStartIndex + idQualifier.length());
+ return viewIdResourceName.substring(unqualifiedNameStartIndex);
+ }
+ return getSimpleClassName(nodeInfo);
+ }
+
+ private static CharSequence getSimpleClassName(AccessibilityNodeInfo nodeInfo) {
+ CharSequence name = nodeInfo.getClassName();
+ for (int i = name.length() - 1; i > 0; i--) {
+ char ithChar = name.charAt(i);
+ if (ithChar == '.' || ithChar == '$') {
+ return name.subSequence(i + 1, name.length());
+ }
+ }
+ return name;
+ }
+
+ private AccessibilityNodePathBuilder() {
+ }
+}
diff --git a/core/java/android/view/accessibility/a11ychecker/Android.bp b/core/java/android/view/accessibility/a11ychecker/Android.bp
new file mode 100644
index 0000000..e5a577c
--- /dev/null
+++ b/core/java/android/view/accessibility/a11ychecker/Android.bp
@@ -0,0 +1,7 @@
+java_library_static {
+ name: "A11yChecker",
+ srcs: [
+ "*.java",
+ ],
+ visibility: ["//visibility:public"],
+}
diff --git a/core/java/android/view/accessibility/a11ychecker/OWNERS b/core/java/android/view/accessibility/a11ychecker/OWNERS
new file mode 100644
index 0000000..d1e7986
--- /dev/null
+++ b/core/java/android/view/accessibility/a11ychecker/OWNERS
@@ -0,0 +1,4 @@
+# Android Accessibility Framework owners
+include /services/accessibility/OWNERS
+
+yaraabdullatif@google.com
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 95d001f..d0bc57b 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -100,6 +100,20 @@
flag {
namespace: "accessibility"
+ name: "global_action_menu"
+ description: "Allow AccessibilityService to perform GLOBAL_ACTION_MENU"
+ bug: "334954140"
+}
+
+flag {
+ namespace: "accessibility"
+ name: "global_action_media_play_pause"
+ description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE"
+ bug: "334954140"
+}
+
+flag {
+ namespace: "accessibility"
name: "granular_scrolling"
is_exported: true
description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3c5623f..9512347 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,8 +17,10 @@
package android.widget;
import static android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL;
+import static android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO;
import static android.appwidget.flags.Flags.drawDataParcel;
import static android.appwidget.flags.Flags.remoteAdapterConversion;
+import static android.util.proto.ProtoInputStream.NO_MORE_FIELDS;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
import android.annotation.AttrRes;
@@ -60,6 +62,7 @@
import android.content.res.loader.ResourcesLoader;
import android.content.res.loader.ResourcesProvider;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.BlendMode;
import android.graphics.Outline;
import android.graphics.PorterDuff;
@@ -88,12 +91,17 @@
import android.util.IntArray;
import android.util.Log;
import android.util.LongArray;
+import android.util.LongSparseArray;
import android.util.Pair;
import android.util.SizeF;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.ProtoUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
@@ -135,10 +143,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1231,8 +1241,8 @@
/**
* @hide
*/
- public CompletableFuture<Void> collectAllIntents() {
- return mCollectionCache.collectAllIntentsNoComplete(this);
+ public CompletableFuture<Void> collectAllIntents(int bitmapSizeLimit) {
+ return mCollectionCache.collectAllIntentsNoComplete(this, bitmapSizeLimit);
}
private class RemoteCollectionCache {
@@ -1259,11 +1269,16 @@
int intentId = in.readInt();
String intentUri = in.readString8();
RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData);
- mIdToUriMapping.put(intentId, intentUri);
- mUriToCollectionMapping.put(intentUri, items);
+ addMapping(intentId, intentUri, items);
}
}
+ void addMapping(int intentId, String intentUri, RemoteCollectionItems items) {
+ mIdToUriMapping.put(intentId, intentUri);
+ mUriToCollectionMapping.put(intentUri, items);
+ }
+
+
void setHierarchyDataForId(int intentId, HierarchyRootData data) {
String uri = mIdToUriMapping.get(intentId);
if (mUriToCollectionMapping.get(uri) == null) {
@@ -1281,7 +1296,7 @@
}
public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
- @NonNull RemoteViews inViews) {
+ @NonNull RemoteViews inViews, int bitmapSizeLimit) {
SparseArray<Intent> idToIntentMapping = new SparseArray<>();
// Collect the number of uinque Intent (which is equal to the number of new connections
// to make) for size allocation and exclude certain collections from being written to
@@ -1309,7 +1324,11 @@
? 0
: remainingSize / numOfIntents;
- return connectAllUniqueIntents(individualSize, idToIntentMapping);
+ int individualBitmapSizeLimit = (bitmapSizeLimit - getBitmapMemoryUsedByActions())
+ / numOfIntents;
+
+ return connectAllUniqueIntents(individualSize, individualBitmapSizeLimit,
+ idToIntentMapping);
}
private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
@@ -1375,13 +1394,13 @@
}
private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
- @NonNull SparseArray<Intent> idToIntentMapping) {
+ int individualBitmapSize, @NonNull SparseArray<Intent> idToIntentMapping) {
List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
for (int i = 0; i < idToIntentMapping.size(); i++) {
String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
Intent currentIntent = idToIntentMapping.valueAt(i);
intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
- individualSize)
+ individualSize, individualBitmapSize)
.thenAccept(items -> {
items.setHierarchyRootData(getHierarchyRootData());
mUriToCollectionMapping.put(currentIntentUri, items);
@@ -1392,7 +1411,7 @@
}
private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent, int individualSize) {
+ Intent intent, int individualSize, int individualBitmapSize) {
if (intent == null) {
Log.e(LOG_TAG, "Null intent received when generating adapter future");
return CompletableFuture.completedFuture(new RemoteCollectionItems
@@ -1410,7 +1429,8 @@
RemoteCollectionItems items;
try {
items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems(individualSize);
+ .getRemoteCollectionItems(individualSize,
+ individualBitmapSize);
} catch (RemoteException re) {
items = new RemoteCollectionItems.Builder().build();
Log.e(LOG_TAG, "Error getting collection items from the"
@@ -1453,6 +1473,87 @@
mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
}
}
+
+ public void writeToProto(Context context, ProtoOutputStream out) {
+ final long token = out.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE);
+ for (int i = 0; i < mIdToUriMapping.size(); i++) {
+ final long entryToken = out.start(RemoteViewsProto.RemoteCollectionCache.ENTRIES);
+ out.write(RemoteViewsProto.RemoteCollectionCache.Entry.ID,
+ mIdToUriMapping.keyAt(i));
+ String intentUri = mIdToUriMapping.valueAt(i);
+ out.write(RemoteViewsProto.RemoteCollectionCache.Entry.URI, intentUri);
+ final long itemsToken = out.start(
+ RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS);
+ mUriToCollectionMapping.get(intentUri).writeToProto(context, out, /* attached= */
+ true);
+ out.end(itemsToken);
+ out.end(entryToken);
+ }
+ out.end(token);
+ }
+ }
+
+ private PendingResources<RemoteCollectionCache> populateRemoteCollectionCacheFromProto(
+ ProtoInputStream in) throws Exception {
+ final ArrayList<LongSparseArray<Object>> entries = new ArrayList<>();
+ final long token = in.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.RemoteCollectionCache.ENTRIES:
+ final LongSparseArray<Object> entry = new LongSparseArray<>();
+ final long entryToken = in.start(
+ RemoteViewsProto.RemoteCollectionCache.ENTRIES);
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ID:
+ entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ID,
+ in.readInt(
+ RemoteViewsProto.RemoteCollectionCache.Entry.ID));
+ break;
+ case (int) RemoteViewsProto.RemoteCollectionCache.Entry.URI:
+ entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.URI,
+ in.readString(
+ RemoteViewsProto.RemoteCollectionCache.Entry.URI));
+ break;
+ case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS:
+ final long itemsToken = in.start(
+ RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS);
+ entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS,
+ RemoteCollectionItems.createFromProto(in));
+ in.end(itemsToken);
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(entryToken);
+ checkContainsKeys(entry,
+ new long[]{RemoteViewsProto.RemoteCollectionCache.Entry.ID,
+ RemoteViewsProto.RemoteCollectionCache.Entry.URI,
+ RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS});
+ entries.add(entry);
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ in.end(token);
+
+ return (context, resources, rootData, depth) -> {
+ for (LongSparseArray<Object> entry : entries) {
+ int id = (int) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.ID);
+ String uri = (String) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.URI);
+ // Depth resets to 0 for RemoteCollectionItems
+ RemoteCollectionItems items = ((PendingResources<RemoteCollectionItems>) entry.get(
+ RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS)).create(context,
+ resources, rootData, depth);
+ rootData.mRemoteCollectionCache.addMapping(id, uri, items);
+ }
+ // Redundant return, but type signature requires we return something.
+ return rootData.mRemoteCollectionCache;
+ };
}
private class SetRemoteViewsAdapterIntent extends Action {
@@ -2002,7 +2103,14 @@
}
}
- private static class BitmapCache {
+ /**
+ * @hide
+ */
+ @NonNull BitmapCache getBitmapCache() {
+ return mBitmapCache;
+ }
+
+ static class BitmapCache {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ArrayList<Bitmap> mBitmaps;
SparseIntArray mBitmapHashes;
@@ -2024,6 +2132,11 @@
}
}
+ BitmapCache(BitmapCache other) {
+ mBitmaps = new ArrayList<>(other.mBitmaps);
+ mBitmapHashes = other.mBitmapHashes.clone();
+ }
+
public int getBitmapId(Bitmap b) {
if (b == null) {
return -1;
@@ -2056,6 +2169,15 @@
dest.writeTypedList(mBitmaps, flags);
}
+ public void writeBitmapsToProto(ProtoOutputStream out) {
+ for (int i = 0; i < mBitmaps.size(); i++) {
+ final Bitmap bitmap = mBitmaps.get(i);
+ final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, bytes);
+ out.write(RemoteViewsProto.BITMAP_CACHE, bytes.toByteArray());
+ }
+ }
+
public int getBitmapMemory() {
if (mBitmapMemory < 0) {
mBitmapMemory = 0;
@@ -2066,6 +2188,12 @@
}
return mBitmapMemory;
}
+
+ public void mergeWithCache(BitmapCache other) {
+ for (int i = 0; i < other.mBitmaps.size(); i++) {
+ getBitmapId(other.mBitmaps.get(i));
+ }
+ }
}
private class BitmapReflectionAction extends Action {
@@ -7338,6 +7466,38 @@
return true;
}
+ private int getBitmapMemoryUsedByActions() {
+ Set<Integer> bitmapIdSet = getBitmapIdsUsedByActions(new HashSet<>());
+ int result = 0;
+ for (int bitmapId: bitmapIdSet) {
+ result += mBitmapCache.getBitmapForId(bitmapId).getAllocationByteCount();
+ }
+
+ return result;
+ }
+
+ private Set<Integer> getBitmapIdsUsedByActions(@NonNull Set<Integer> intSet) {
+ if (hasSizedRemoteViews()) {
+ for (RemoteViews views: mSizedRemoteViews) {
+ views.getBitmapIdsUsedByActions(intSet);
+ }
+ } else if (hasLandscapeAndPortraitLayouts()) {
+ mLandscape.getBitmapIdsUsedByActions(intSet);
+ mPortrait.getBitmapIdsUsedByActions(intSet);
+ } else if (mActions != null) {
+ for (Action action: mActions) {
+ if (action instanceof ViewGroupActionAdd vgaa
+ && vgaa.mNestedViews != null) {
+ vgaa.mNestedViews.getBitmapIdsUsedByActions(intSet);
+ } else if (action instanceof BitmapReflectionAction bitmapAction) {
+ intSet.add(bitmapAction.mBitmapId);
+ }
+ }
+ }
+
+ return intSet;
+ }
+
/** Representation of a fixed list of items to be displayed in a RemoteViews collection. */
public static final class RemoteCollectionItems implements Parcelable {
private final long[] mIds;
@@ -7460,6 +7620,127 @@
dest.restoreAllowSquashing(prevAllowSquashing);
}
+ /** @hide */
+ public void writeToProto(Context context, ProtoOutputStream out) {
+ writeToProto(context, out, /* attached= */ false);
+ }
+
+ private void writeToProto(Context context, ProtoOutputStream out, boolean attached) {
+ for (long id : mIds) {
+ out.write(RemoteViewsProto.RemoteCollectionItems.IDS, id);
+ }
+
+ boolean restoreRoot = false;
+ out.write(RemoteViewsProto.RemoteCollectionItems.ATTACHED, attached);
+ if (!attached && mViews.length > 0 && !mViews[0].mIsRoot) {
+ restoreRoot = true;
+ mViews[0].mIsRoot = true;
+ }
+ for (RemoteViews view : mViews) {
+ final long viewsToken = out.start(RemoteViewsProto.RemoteCollectionItems.VIEWS);
+ view.writePreviewToProto(context, out);
+ out.end(viewsToken);
+ }
+ if (restoreRoot) mViews[0].mIsRoot = false;
+ out.write(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, mHasStableIds);
+ out.write(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, mViewTypeCount);
+ }
+
+ /**
+ * Overload used for testing unattached RemoteCollectionItems serialization.
+ *
+ * @hide
+ */
+ public static RemoteCollectionItems createFromProto(Context context, ProtoInputStream in)
+ throws Exception {
+ return createFromProto(in).create(context, context.getResources(), /* rootData= */
+ null, 0);
+ }
+
+ /** @hide */
+ public static PendingResources<RemoteCollectionItems> createFromProto(ProtoInputStream in)
+ throws Exception {
+ final LongSparseArray<Object> values = new LongSparseArray<>();
+ values.put(RemoteViewsProto.RemoteCollectionItems.IDS, new ArrayList<Long>());
+ values.put(RemoteViewsProto.RemoteCollectionItems.VIEWS,
+ new ArrayList<PendingResources<RemoteViews>>());
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.RemoteCollectionItems.IDS:
+ ((ArrayList<Long>) values.get(
+ RemoteViewsProto.RemoteCollectionItems.IDS)).add(
+ in.readLong(RemoteViewsProto.RemoteCollectionItems.IDS));
+ break;
+ case (int) RemoteViewsProto.RemoteCollectionItems.VIEWS:
+ final long viewsToken = in.start(
+ RemoteViewsProto.RemoteCollectionItems.VIEWS);
+ ((ArrayList<PendingResources<RemoteViews>>) values.get(
+ RemoteViewsProto.RemoteCollectionItems.VIEWS)).add(
+ RemoteViews.createFromProto(in));
+ in.end(viewsToken);
+ break;
+ case (int) RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS:
+ values.put(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS,
+ in.readBoolean(
+ RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS));
+ break;
+ case (int) RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT:
+ values.put(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT,
+ in.readInt(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT));
+ break;
+ case (int) RemoteViewsProto.RemoteCollectionItems.ATTACHED:
+ values.put(RemoteViewsProto.RemoteCollectionItems.ATTACHED,
+ in.readBoolean(RemoteViewsProto.RemoteCollectionItems.ATTACHED));
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+
+ checkContainsKeys(values,
+ new long[]{RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT});
+ return (context, resources, rootData, depth) -> {
+ List<Long> idList = (List<Long>) values.get(
+ RemoteViewsProto.RemoteCollectionItems.IDS);
+ long[] ids = new long[idList.size()];
+ for (int i = 0; i < idList.size(); i++) {
+ ids[i] = idList.get(i);
+ }
+ boolean attached = (boolean) values.get(
+ RemoteViewsProto.RemoteCollectionItems.ATTACHED, false);
+ List<PendingResources<RemoteViews>> pendingViews =
+ (List<PendingResources<RemoteViews>>) values.get(
+ RemoteViewsProto.RemoteCollectionItems.VIEWS);
+ RemoteViews[] views = new RemoteViews[pendingViews.size()];
+
+ if (attached && rootData == null) {
+ throw new IllegalStateException("Cannot create a RemoteCollectionItems from "
+ + "proto that was attached without providing HierarchyRootData");
+ }
+
+ int firstChildIndex = 0;
+ if (!attached && pendingViews.size() > 0) {
+ // If written as unattached, get HierarchyRootData from first view
+ views[0] = pendingViews.get(0).create(context, resources, /* rootData= */ null,
+ /* depth= */ 0);
+ rootData = views[0].getHierarchyRootData();
+ firstChildIndex = 1;
+ }
+ for (int i = firstChildIndex; i < views.length; i++) {
+ // Depth is reset to 0 for RemoteCollectionItems item views, see Parcel
+ // constructor.
+ views[i] = pendingViews.get(i).create(context, resources, rootData,
+ /* depth= */ 0);
+ }
+ return new RemoteCollectionItems(ids, views,
+ (boolean) values.get(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS,
+ false),
+ (int) values.get(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT,
+ 0));
+ };
+ }
+
/**
* Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id
* should be considered meaningful across collection updates.
@@ -7822,4 +8103,308 @@
mClassCookies = classCookies;
}
}
+
+ /**
+ * Write this RemoteViews to proto.
+ * @hide
+ */
+ @FlaggedApi(FLAG_REMOTE_VIEWS_PROTO)
+ public void writePreviewToProto(@NonNull Context context, @NonNull ProtoOutputStream out) {
+ if (mApplication != null) {
+ // mApplication may be null if this was created with DrawInstructions constructor.
+ out.write(RemoteViewsProto.PACKAGE_NAME, mApplication.packageName);
+ }
+ Resources appResources = getContextForResourcesEnsuringCorrectCachedApkPaths(
+ context).getResources();
+ if (mLayoutId != 0) {
+ out.write(RemoteViewsProto.LAYOUT_ID, appResources.getResourceName(mLayoutId));
+ }
+ if (mLightBackgroundLayoutId != 0) {
+ out.write(RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID,
+ appResources.getResourceName(mLightBackgroundLayoutId));
+ }
+ if (mViewId != 0 && mViewId != -1) {
+ out.write(RemoteViewsProto.VIEW_ID, appResources.getResourceName(mViewId));
+ }
+ if (mIsRoot) {
+ mBitmapCache.writeBitmapsToProto(out);
+ mCollectionCache.writeToProto(context, out);
+ }
+ out.write(RemoteViewsProto.IS_ROOT, mIsRoot);
+ out.write(RemoteViewsProto.APPLY_FLAGS, mApplyFlags);
+ out.write(RemoteViewsProto.HAS_DRAW_INSTRUCTIONS, mHasDrawInstructions);
+ if (mProviderInstanceId != -1) {
+ out.write(RemoteViewsProto.PROVIDER_INSTANCE_ID, mProviderInstanceId);
+ }
+
+ if (!hasMultipleLayouts()) {
+ out.write(RemoteViewsProto.MODE, MODE_NORMAL);
+ if (mIdealSize != null) {
+ final long token = out.start(RemoteViewsProto.IDEAL_SIZE);
+ out.write(SizeFProto.WIDTH, mIdealSize.getWidth());
+ out.write(SizeFProto.HEIGHT, mIdealSize.getHeight());
+ out.end(token);
+ }
+ } else if (hasSizedRemoteViews()) {
+ out.write(RemoteViewsProto.MODE, MODE_HAS_SIZED_REMOTEVIEWS);
+ for (RemoteViews view : mSizedRemoteViews) {
+ final long sizedViewToken = out.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
+ view.writePreviewToProto(context, out);
+ out.end(sizedViewToken);
+ }
+ } else {
+ out.write(RemoteViewsProto.MODE, MODE_HAS_LANDSCAPE_AND_PORTRAIT);
+ final long landscapeViewToken = out.start(RemoteViewsProto.LANDSCAPE_REMOTEVIEWS);
+ mLandscape.writePreviewToProto(context, out);
+ out.end(landscapeViewToken);
+ final long portraitViewToken = out.start(RemoteViewsProto.PORTRAIT_REMOTEVIEWS);
+ mPortrait.writePreviewToProto(context, out);
+ out.end(portraitViewToken);
+ }
+ }
+
+ /**
+ * Create a RemoteViews from proto input.
+ * @hide
+ */
+ @FlaggedApi(FLAG_REMOTE_VIEWS_PROTO)
+ public static RemoteViews createPreviewFromProto(Context context, ProtoInputStream in)
+ throws Exception {
+ return createFromProto(in).create(context, context.getResources(), /* rootData= */ null,
+ /* depth= */ 0);
+ }
+
+ private static PendingResources<RemoteViews> createFromProto(ProtoInputStream in)
+ throws Exception {
+ // Grouping these variables into an anonymous object allows us to access them through `ref`
+ // (which is final) later in the lambda.
+ final var ref = new Object() {
+ final RemoteViews mRv = new RemoteViews();
+ int mMode = 0;
+ int mApplyFlags = 0;
+ long mProviderInstanceId = -1;
+ String mPackageName = null;
+ SizeF mIdealSize = null;
+ String mLayoutResName = null;
+ String mLightBackgroundResName = null;
+ String mViewResName = null;
+ final List<PendingResources<RemoteViews>> mSizedRemoteViews = new ArrayList<>();
+ PendingResources<RemoteViews> mLandscapeViews = null;
+ PendingResources<RemoteViews> mPortraitViews = null;
+ PendingResources<RemoteCollectionCache> mPopulateRemoteCollectionCache = null;
+ boolean mIsRoot = false;
+ boolean mHasDrawInstructions = false;
+ };
+
+ try {
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) RemoteViewsProto.MODE:
+ ref.mMode = in.readInt(RemoteViewsProto.MODE);
+ break;
+ case (int) RemoteViewsProto.PACKAGE_NAME:
+ ref.mPackageName = in.readString(RemoteViewsProto.PACKAGE_NAME);
+ break;
+ case (int) RemoteViewsProto.IDEAL_SIZE:
+ final long idealSizeToken = in.start(RemoteViewsProto.IDEAL_SIZE);
+ ref.mIdealSize = createSizeFFromProto(in);
+ in.end(idealSizeToken);
+ break;
+ case (int) RemoteViewsProto.LAYOUT_ID:
+ ref.mLayoutResName = in.readString(RemoteViewsProto.LAYOUT_ID);
+ break;
+ case (int) RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID:
+ ref.mLightBackgroundResName = in.readString(
+ RemoteViewsProto.LIGHT_BACKGROUND_LAYOUT_ID);
+ break;
+ case (int) RemoteViewsProto.VIEW_ID:
+ ref.mViewResName = in.readString(RemoteViewsProto.VIEW_ID);
+ break;
+ case (int) RemoteViewsProto.APPLY_FLAGS:
+ ref.mApplyFlags = in.readInt(RemoteViewsProto.APPLY_FLAGS);
+ break;
+ case (int) RemoteViewsProto.PROVIDER_INSTANCE_ID:
+ ref.mProviderInstanceId = in.readInt(RemoteViewsProto.PROVIDER_INSTANCE_ID);
+ break;
+ case (int) RemoteViewsProto.SIZED_REMOTEVIEWS:
+ final long sizedToken = in.start(RemoteViewsProto.SIZED_REMOTEVIEWS);
+ ref.mSizedRemoteViews.add(createFromProto(in));
+ in.end(sizedToken);
+ break;
+ case (int) RemoteViewsProto.LANDSCAPE_REMOTEVIEWS:
+ final long landscapeToken = in.start(
+ RemoteViewsProto.LANDSCAPE_REMOTEVIEWS);
+ ref.mLandscapeViews = createFromProto(in);
+ in.end(landscapeToken);
+ break;
+ case (int) RemoteViewsProto.PORTRAIT_REMOTEVIEWS:
+ final long portraitToken = in.start(RemoteViewsProto.PORTRAIT_REMOTEVIEWS);
+ ref.mPortraitViews = createFromProto(in);
+ in.end(portraitToken);
+ break;
+ case (int) RemoteViewsProto.BITMAP_CACHE:
+ byte[] src = in.readBytes(RemoteViewsProto.BITMAP_CACHE);
+ Bitmap bitmap = BitmapFactory.decodeByteArray(src, 0, src.length);
+ ref.mRv.mBitmapCache.getBitmapId(bitmap);
+ break;
+ case (int) RemoteViewsProto.REMOTE_COLLECTION_CACHE:
+ final long collectionToken = in.start(
+ RemoteViewsProto.REMOTE_COLLECTION_CACHE);
+ ref.mPopulateRemoteCollectionCache =
+ ref.mRv.populateRemoteCollectionCacheFromProto(in);
+ in.end(collectionToken);
+ break;
+ case (int) RemoteViewsProto.IS_ROOT:
+ ref.mIsRoot = in.readBoolean(RemoteViewsProto.IS_ROOT);
+ break;
+ case (int) RemoteViewsProto.HAS_DRAW_INSTRUCTIONS:
+ ref.mHasDrawInstructions = in.readBoolean(
+ RemoteViewsProto.HAS_DRAW_INSTRUCTIONS);
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return (context, resources, rootData, depth) -> {
+ if (depth > MAX_NESTED_VIEWS && (UserHandle.getAppId(Binder.getCallingUid())
+ != Process.SYSTEM_UID)) {
+ throw new IllegalArgumentException("Too many nested views.");
+ }
+ depth++;
+
+ RemoteViews rv = ref.mRv;
+ rv.mApplyFlags = ref.mApplyFlags;
+ rv.mIsRoot = ref.mIsRoot;
+ rv.mHasDrawInstructions = ref.mHasDrawInstructions;
+
+ // The root view will read its HierarchyRootData (bitmap cache, collection cache) from
+ // proto; all nested views will instead get it through the rootData parameter.
+ if (rootData == null) {
+ if (!rv.mIsRoot || depth != 1) {
+ throw new IllegalStateException(
+ "A nested view did not receive HierarchyRootData");
+ }
+ rootData = rv.getHierarchyRootData();
+ } else {
+ rv.configureAsChild(rootData);
+ }
+
+ Context appContext = null;
+ Resources appResources = null;
+ if (!ref.mHasDrawInstructions) {
+ checkProtoResultNotNull(ref.mPackageName, "No application info");
+ rv.mApplication = context.getPackageManager().getApplicationInfo(ref.mPackageName,
+ /* flags= */ 0);
+ appContext = rv.getContextForResourcesEnsuringCorrectCachedApkPaths(context);
+ appResources = appContext.getResources();
+
+ checkProtoResultNotNull(ref.mLayoutResName, "No layout id");
+ rv.mLayoutId = appResources.getIdentifier(ref.mLayoutResName, /* defType= */ null,
+ /* defPackage= */ null);
+ checkValidResource(rv.mLayoutId, "Invalid layout id", ref.mLayoutResName);
+
+ if (ref.mViewResName != null) {
+ rv.mViewId = appResources.getIdentifier(ref.mViewResName, /* defType= */ null,
+ /* defPackage= */ null);
+ checkValidResource(rv.mViewId, "Invalid view id", ref.mViewResName);
+ }
+
+ if (ref.mLightBackgroundResName != null) {
+ int lightBackgroundLayoutId = appResources.getIdentifier(
+ ref.mLightBackgroundResName,
+ /* defType= */ null, /* defPackage= */ null);
+ checkValidResource(lightBackgroundLayoutId,
+ "Invalid light background layout id", ref.mLightBackgroundResName);
+ rv.setLightBackgroundLayoutId(lightBackgroundLayoutId);
+ }
+ }
+ if (ref.mPopulateRemoteCollectionCache != null) {
+ ref.mPopulateRemoteCollectionCache.create(context, resources, rootData, depth);
+ }
+ if (ref.mProviderInstanceId != -1) {
+ rv.mProviderInstanceId = ref.mProviderInstanceId;
+ }
+ if (ref.mMode == MODE_NORMAL) {
+ rv.setIdealSize(ref.mIdealSize);
+ return rv;
+ } else if (ref.mMode == MODE_HAS_SIZED_REMOTEVIEWS) {
+ List<RemoteViews> sizedViews = new ArrayList<>();
+ for (RemoteViews.PendingResources<RemoteViews> pendingViews :
+ ref.mSizedRemoteViews) {
+ RemoteViews views = pendingViews.create(context, resources, rootData, depth);
+ sizedViews.add(views);
+ }
+ rv.initializeSizedRemoteViews(sizedViews.iterator());
+ return rv;
+ } else if (ref.mMode == MODE_HAS_LANDSCAPE_AND_PORTRAIT) {
+ checkProtoResultNotNull(ref.mLandscapeViews, "Missing landscape views");
+ checkProtoResultNotNull(ref.mPortraitViews, "Missing portrait views");
+ RemoteViews parentRv = new RemoteViews(
+ ref.mLandscapeViews.create(context, resources, rootData, depth),
+ ref.mPortraitViews.create(context, resources, rootData, depth));
+ parentRv.initializeFrom(/* src= */ rv, /* hierarchyRoot= */ rv);
+ return parentRv;
+ } else {
+ throw new InvalidProtoException(ref.mMode + " is not a valid mode.");
+ }
+ };
+ }
+
+ private static class InvalidProtoException extends Exception {
+ InvalidProtoException(String message) {
+ super(message);
+ }
+ }
+
+ private interface PendingResources<T> {
+ T create(Context context, Resources appResources, HierarchyRootData rootData, int depth)
+ throws Exception;
+ }
+
+ private static void checkValidResource(int id, String message, String resName)
+ throws Exception {
+ if (id == 0) throw new Exception(message + ": " + resName);
+ }
+
+ private static void checkProtoResultNotNull(Object o, String message)
+ throws InvalidProtoException {
+ if (o == null) {
+ throw new InvalidProtoException(message);
+ }
+ }
+
+ private static void checkContainsKeys(LongSparseArray<?> array, long[] requiredFields) {
+ for (long requiredField : requiredFields) {
+ if (array.indexOfKey(requiredField) < 0) {
+ throw new IllegalArgumentException(
+ "RemoteViews proto missing field: " + ProtoStream.getFieldIdString(
+ requiredField));
+ }
+ }
+ }
+
+ private static SizeF createSizeFFromProto(ProtoInputStream in) throws Exception {
+ float width = 0;
+ float height = 0;
+ while (in.nextField() != NO_MORE_FIELDS) {
+ switch (in.getFieldNumber()) {
+ case (int) SizeFProto.WIDTH:
+ width = in.readFloat(SizeFProto.WIDTH);
+ break;
+ case (int) SizeFProto.HEIGHT:
+ height = in.readFloat(SizeFProto.HEIGHT);
+ break;
+ default:
+ Log.w(LOG_TAG, "Unhandled field while reading SizeF proto!\n"
+ + ProtoUtils.currentFieldToString(in));
+ }
+ }
+
+ return new SizeF(width, height);
+ }
}
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index c79eac6..4045d42 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -128,7 +128,8 @@
/**
* @hide
*/
- default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+ default RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+ int capBitmapSize) {
RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
.Builder().build();
Parcel capSizeTestParcel = Parcel.obtain();
@@ -138,6 +139,7 @@
try {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
new RemoteViews.RemoteCollectionItems.Builder();
+ RemoteViews.BitmapCache testBitmapCache = null;
onDataSetChanged();
itemsBuilder.setHasStableIds(hasStableIds());
@@ -150,6 +152,15 @@
if (capSizeTestParcel.dataSize() > capSize) {
break;
}
+ if (testBitmapCache == null) {
+ testBitmapCache = new RemoteViews.BitmapCache(currentView.getBitmapCache());
+ } else {
+ testBitmapCache.mergeWithCache(currentView.getBitmapCache());
+ }
+ if (testBitmapCache.getBitmapMemory() >= capBitmapSize) {
+ break;
+ }
+
itemsBuilder.addItem(currentItemId, currentView);
}
@@ -266,11 +277,12 @@
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+ int capBitmapSize) {
RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
.Builder().build();
try {
- items = mFactory.getRemoteCollectionItems(capSize);
+ items = mFactory.getRemoteCollectionItems(capSize, capBitmapSize);
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index d5398e6..781a901 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.util.SequenceUtils.getInitSeq;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -53,6 +55,9 @@
public float compatScale = 1f;
+ /** To make sure the info update between client and system server is in order. */
+ public int seq = getInitSeq();
+
public ClientWindowFrames() {
}
@@ -74,6 +79,7 @@
}
isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout;
compatScale = other.compatScale;
+ seq = other.seq;
}
/** Needed for AIDL out parameters. */
@@ -84,6 +90,7 @@
attachedFrame = in.readTypedObject(Rect.CREATOR);
isParentFrameClippedByDisplayCutout = in.readBoolean();
compatScale = in.readFloat();
+ seq = in.readInt();
}
@Override
@@ -94,6 +101,7 @@
dest.writeTypedObject(attachedFrame, flags);
dest.writeBoolean(isParentFrameClippedByDisplayCutout);
dest.writeFloat(compatScale);
+ dest.writeInt(seq);
}
@Override
@@ -116,6 +124,7 @@
return false;
}
final ClientWindowFrames other = (ClientWindowFrames) o;
+ // seq is for internal bookkeeping only.
return frame.equals(other.frame)
&& displayFrame.equals(other.displayFrame)
&& parentFrame.equals(other.parentFrame)
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index cc54a93..15adc80 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -19,8 +19,6 @@
import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
-import static com.android.window.flags.Flags.windowTokenConfigThreadSafe;
-
import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -146,7 +144,7 @@
if (context == null) {
return;
}
- if (shouldReportConfigChange && windowTokenConfigThreadSafe()) {
+ if (shouldReportConfigChange) {
// Only report to ClientTransactionListenerController when shouldReportConfigChange.
final ClientTransactionListenerController controller =
getClientTransactionListenerController();
diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
index fd73d35..0472b6c4 100644
--- a/core/java/android/window/flags/OWNERS
+++ b/core/java/android/window/flags/OWNERS
@@ -1,3 +1,4 @@
per-file responsible_apis.aconfig = file:/BAL_OWNERS
per-file large_screen_experiences_app_compat.aconfig = file:/LSE_APP_COMPAT_OWNERS
per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS
+per-file lse_desktop_experience.aconfig = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index ca125da..d0ab674 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -120,3 +120,10 @@
description: "Whether to enable min/max window size constraints when resizing a window in desktop windowing mode"
bug: "327589741"
}
+
+flag {
+ name: "show_desktop_windowing_dev_option"
+ namespace: "lse_desktop_experience"
+ description: "Whether to show developer option for enabling desktop windowing mode"
+ bug: "348193756"
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 69cac6f..94f6503 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -56,3 +56,11 @@
description: "Improved metrics."
bug: "339245692"
}
+
+flag {
+ name: "bal_send_intent_with_options"
+ namespace: "responsible_apis"
+ description: "Add options parameter to IntentSender.sendIntent."
+ bug: "339720406"
+}
+
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index b714682..b5bc572 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -53,17 +53,6 @@
}
flag {
- name: "remove_prepare_surface_in_placement"
- namespace: "windowing_frontend"
- description: "Reduce unnecessary invocation to improve performance"
- bug: "330721336"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "close_to_square_config_includes_status_bar"
namespace: "windowing_frontend"
description: "On close to square display, when necessary, configuration includes status bar"
@@ -146,6 +135,13 @@
}
flag {
+ name: "process_priority_policy_for_multi_window_mode"
+ namespace: "windowing_frontend"
+ description: "Use higher priority for top-like processes"
+ bug: "200769420"
+}
+
+flag {
name: "insets_decoupled_configuration"
namespace: "windowing_frontend"
description: "Configuration decoupled from insets"
@@ -192,4 +188,15 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "ensure_wallpaper_in_transitions"
+ namespace: "windowing_frontend"
+ description: "Ensure that wallpaper window tokens are always present/available for collection in transitions"
+ bug: "347593088"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 0a4762d..8cd2a3e 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -93,16 +93,6 @@
flag {
namespace: "windowing_sdk"
- name: "window_token_config_thread_safe"
- description: "Ensure the Configuration pre/post changed is thread safe"
- bug: "334285008"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "windowing_sdk"
name: "always_defer_transition_when_apply_wct"
description: "Report error when defer transition fails when it should not"
bug: "335562144"
@@ -113,17 +103,6 @@
flag {
namespace: "windowing_sdk"
- name: "window_session_relayout_info"
- description: "Pass an out RelayoutInfo instead of Bundle to fix the Parcel recycle bug"
- bug: "335601427"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "windowing_sdk"
name: "fix_pip_restore_to_overlay"
description: "Restore exit-pip activity back to ActivityEmbedding overlay"
bug: "297887697"
@@ -191,3 +170,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "fix_no_container_update_without_resize"
+ description: "Fix the containers not being updated when the Task is brought to front and has the same configuration"
+ bug: "344721335"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
index fc3cd45..c8d6194 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityButtonChooserActivity.java
@@ -29,8 +29,6 @@
import android.content.ComponentName;
import android.os.Bundle;
import android.provider.Settings;
-import android.text.TextUtils;
-import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.GridView;
import android.widget.TextView;
@@ -73,16 +71,11 @@
promptPrologue.setText(isTouchExploreOn
? R.string.accessibility_gesture_3finger_prompt_text
: R.string.accessibility_gesture_prompt_text);
- }
- if (TextUtils.isEmpty(component)) {
final TextView prompt = findViewById(R.id.accessibility_button_prompt);
- if (isGestureNavigateEnabled) {
- prompt.setText(isTouchExploreOn
- ? R.string.accessibility_gesture_3finger_instructional_text
- : R.string.accessibility_gesture_instructional_text);
- }
- prompt.setVisibility(View.VISIBLE);
+ prompt.setText(isTouchExploreOn
+ ? R.string.accessibility_gesture_3finger_instructional_text
+ : R.string.accessibility_gesture_instructional_text);
}
mTargets.addAll(getTargets(this, SOFTWARE));
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
index 66faa31..ec90dd2 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
@@ -36,6 +36,7 @@
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder;
+import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Set;
@@ -67,6 +68,10 @@
public AccessibilityTarget(Context context, @UserShortcutType int shortcutType,
@AccessibilityFragmentType int fragmentType, boolean isShortcutSwitched, String id,
int uid, CharSequence label, Drawable icon, String key) {
+ if (!isRecognizedShortcutType(shortcutType)) {
+ throw new IllegalArgumentException(
+ "Unexpected shortcut type " + ShortcutUtils.convertToKey(shortcutType));
+ }
mContext = context;
mShortcutType = shortcutType;
mFragmentType = fragmentType;
@@ -97,20 +102,15 @@
holder.mStatusView.setVisibility(View.GONE);
}
+ @SuppressLint("MissingPermission")
@Override
public void onSelected() {
final AccessibilityManager am =
getContext().getSystemService(AccessibilityManager.class);
- switch (getShortcutType()) {
- case SOFTWARE:
- am.notifyAccessibilityButtonClicked(getContext().getDisplayId(), getId());
- return;
- case HARDWARE:
- am.performAccessibilityShortcut(getId());
- return;
- default:
- throw new IllegalStateException("Unexpected shortcut type");
+ if (am == null) {
+ return;
}
+ am.performAccessibilityShortcut(getContext().getDisplayId(), mShortcutType, getId());
}
@SuppressLint("MissingPermission")
@@ -188,4 +188,15 @@
public String getKey() {
return mKey;
}
+
+ /**
+ * Determines if the provided shortcut type is valid for use with AccessibilityTargets.
+ * @param shortcutType shortcut type to check.
+ * @return {@code true} if the shortcut type can be used, {@code false} otherwise.
+ */
+ @VisibleForTesting
+ public static boolean isRecognizedShortcutType(@UserShortcutType int shortcutType) {
+ int mask = SOFTWARE | HARDWARE;
+ return (shortcutType != 0 && (shortcutType & mask) == shortcutType);
+ }
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index e523ab0..4ccdf79 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -41,6 +41,7 @@
import com.android.internal.R;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
@@ -159,21 +160,32 @@
final List<AccessibilityTarget> targets = new ArrayList<>(installedServices.size());
for (AccessibilityServiceInfo info : installedServices) {
- final int targetSdk =
- info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion;
- final boolean hasRequestAccessibilityButtonFlag =
- (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
- if ((targetSdk <= Build.VERSION_CODES.Q) && !hasRequestAccessibilityButtonFlag
- && (shortcutType == SOFTWARE)) {
- continue;
+ if (isValidServiceTarget(info, shortcutType)) {
+ targets.add(createAccessibilityServiceTarget(context, shortcutType, info));
}
-
- targets.add(createAccessibilityServiceTarget(context, shortcutType, info));
}
return targets;
}
+ /**
+ * Check for maintaining compatibility on prior versions.
+ * Determines if a given service should be accumulated in a list of installed services.
+ * @param info service info to check.
+ * @param shortcutType type of shortcut to accumulate a list for.
+ * @return {@code true} if the service should be added (always true past version Q),
+ * otherwise {@code false}.
+ */
+ @VisibleForTesting
+ public static boolean isValidServiceTarget(
+ AccessibilityServiceInfo info, @UserShortcutType int shortcutType) {
+ final boolean hasRequestAccessibilityButtonFlag =
+ (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ return (info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
+ > Build.VERSION_CODES.Q) || hasRequestAccessibilityButtonFlag
+ || shortcutType != SOFTWARE;
+ }
+
private static List<AccessibilityTarget> getAccessibilityActivityTargets(Context context,
@UserShortcutType int shortcutType) {
final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index e8472d4..0b1ecf7 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -126,7 +126,7 @@
}
/**
- * Changes an accessibility component's state.
+ * Changes an accessibility component's state for the calling process userId
*/
public static void setAccessibilityServiceState(Context context, ComponentName componentName,
boolean enabled) {
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 5b09a8b..6b0ca9f 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -181,6 +181,27 @@
}
/**
+ * Converts {@link Settings.Secure} key to {@link UserShortcutType}.
+ *
+ * @param key The shortcut key in Settings.
+ * @return The mapped type
+ */
+ @UserShortcutType
+ public static int convertToType(String key) {
+ return switch (key) {
+ case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE;
+ case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS;
+ case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE;
+ case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED ->
+ UserShortcutType.TRIPLETAP;
+ case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED ->
+ UserShortcutType.TWOFINGER_DOUBLETAP;
+ default -> throw new IllegalArgumentException(
+ "Unsupported user shortcut key: " + key);
+ };
+ }
+
+ /**
* Updates an accessibility state if the accessibility service is a Always-On a11y service,
* a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON
* <p>
@@ -224,7 +245,9 @@
boolean enableA11yService = servicesWithShortcuts.contains(componentName);
AccessibilityUtils.setAccessibilityServiceState(
context,
- ComponentName.unflattenFromString(componentName), enableA11yService);
+ ComponentName.unflattenFromString(componentName),
+ enableA11yService,
+ userId);
}
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 920981e..a194535 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1209,9 +1209,19 @@
if (!isChangingConfigurations() && mPickOptionRequest != null) {
mPickOptionRequest.cancel();
}
- if (mMultiProfilePagerAdapter != null
- && mMultiProfilePagerAdapter.getActiveListAdapter() != null) {
- mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy();
+ if (mMultiProfilePagerAdapter != null) {
+ ResolverListAdapter activeAdapter =
+ mMultiProfilePagerAdapter.getActiveListAdapter();
+ if (activeAdapter != null) {
+ activeAdapter.onDestroy();
+ }
+ if (android.service.chooser.Flags.fixResolverMemoryLeak()) {
+ ResolverListAdapter inactiveAdapter =
+ mMultiProfilePagerAdapter.getInactiveListAdapter();
+ if (inactiveAdapter != null) {
+ inactiveAdapter.onDestroy();
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index e55cdef..b5930e1 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -72,6 +72,7 @@
boolean bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
IApplicationThread caller, IBinder token, IServiceConnection connection, long flags);
void notifyProviderInheritance(in ComponentName[] componentNames);
+ int getMaxBitmapMemory();
@UnsupportedAppUsage
int[] getAppWidgetIds(in ComponentName providerComponent);
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index ded142c..f611571 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -42,7 +42,7 @@
*
* @hide
*/
-public final class ChangeReporter {
+public class ChangeReporter {
private static final String TAG = "CompatChangeReporter";
private static final Function<Integer, Set<ChangeReport>> NEW_CHANGE_REPORT_SET =
uid -> Collections.synchronizedSet(new HashSet<>());
@@ -88,17 +88,20 @@
* Report the change to stats log and to the debug log if the change was not previously
* logged already.
*
- * @param uid affected by the change
- * @param changeId the reported change id
- * @param state of the reported change - enabled/disabled/only logged
- * @param isLoggableBySdk whether debug logging is allowed for this change based on target
- * SDK version. This is combined with other logic to determine whether to
- * actually log. If the sdk version does not matter, should be true.
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @param isKnownSystemApp do we know that the affected app is a system app? (if true is
+ * definitely a system app, if false it may or may not be a system app)
+ * @param isLoggableBySdk whether debug logging is allowed for this change based on target
+ * SDK version. This is combined with other logic to determine whether
+ * to actually log. If the sdk version does not matter, should be true.
*/
- public void reportChange(int uid, long changeId, int state, boolean isLoggableBySdk) {
+ public void reportChange(int uid, long changeId, int state, boolean isKnownSystemApp,
+ boolean isLoggableBySdk) {
boolean isAlreadyReported =
checkAndSetIsAlreadyReported(uid, new ChangeReport(changeId, state));
- if (!isAlreadyReported) {
+ if (shouldWriteToStatsLog(isKnownSystemApp, isAlreadyReported)) {
FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid,
changeId, state, mSource);
}
@@ -116,7 +119,7 @@
* @param state of the reported change - enabled/disabled/only logged
*/
public void reportChange(int uid, long changeId, int state) {
- reportChange(uid, changeId, state, true);
+ reportChange(uid, changeId, state, false, true);
}
/**
@@ -136,14 +139,15 @@
/**
* Returns whether the next report should be logged to FrameworkStatsLog.
*
- * @param uid affected by the change
- * @param changeId the reported change id
- * @param state of the reported change - enabled/disabled/only logged
+ * @param isKnownSystemApp do we know that the affected app is a system app? (if true is
+ * definitely a system app, if false it may or may not be a system app)
+ * @param isAlreadyReported is the change already reported
* @return true if the report should be logged
*/
@VisibleForTesting
- boolean shouldWriteToStatsLog(int uid, long changeId, int state) {
- return !isAlreadyReported(uid, new ChangeReport(changeId, state));
+ boolean shouldWriteToStatsLog(boolean isKnownSystemApp, boolean isAlreadyReported) {
+ // We don't log where we know the source is a system app or is already reported
+ return !isKnownSystemApp && !isAlreadyReported;
}
/**
@@ -224,6 +228,19 @@
return mReportedChanges.getOrDefault(uid, EMPTY_SET).contains(report);
}
+ /**
+ * Returns whether the next report should be logged.
+ *
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @return true if the report should be logged
+ */
+ @VisibleForTesting
+ boolean isAlreadyReported(int uid, long changeId, int state) {
+ return isAlreadyReported(uid, new ChangeReport(changeId, state));
+ }
+
private void markAsReported(int uid, ChangeReport report) {
mReportedChanges.computeIfAbsent(uid, NEW_CHANGE_REPORT_SET).add(report);
}
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 0068490..9f5ed65 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -78,9 +78,9 @@
// Feature flag that will eventually be removed
private final boolean mIntRangeUserPerceptionEnabled;
- public BrightnessSynchronizer(Context context, boolean intRangeUserPerceptionEnabled) {
- this(context, Looper.getMainLooper(), SystemClock::uptimeMillis,
- intRangeUserPerceptionEnabled);
+ public BrightnessSynchronizer(Context context, Looper looper,
+ boolean intRangeUserPerceptionEnabled) {
+ this(context, looper, SystemClock::uptimeMillis, intRangeUserPerceptionEnabled);
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 6ffa826..ab04851 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -137,9 +137,43 @@
public static final int CUJ_LAUNCHER_PRIVATE_SPACE_LOCK = 102;
public static final int CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK = 103;
+ /**
+ * Track maximize window interaction in desktop mode.
+ *
+ * <p>Tracking starts onClick of the maximize window button or option {@link
+ * com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel#onClick}
+ * and finishes when the window animation is ended {@link
+ * com.android.wm.shell.windowdecor.ToggleResizeDesktopTaskTransitionHandler#startAnimation}
+ */
+ public static final int CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW = 104;
+
+ /**
+ * Track fade-in animation when in SystemUI process fold
+ *
+ * <p>Tracking starts after the screen turns on and finish when the animation is over {@link
+ * com.android.systemui.unfold.FoldLightRevealOverlayAnimation#playFoldLightRevealOverlayAnimation} for the span
+ */
+ public static final int CUJ_FOLD_ANIM = 105;
+
+ /**
+ * Track window re-sizing interaction in desktop mode.
+ */
+ public static final int CUJ_DESKTOP_MODE_RESIZE_WINDOW = 106;
+
+ /** Track entering desktop mode interaction. */
+ public static final int CUJ_DESKTOP_MODE_ENTER_MODE = 107;
+
+ /** Track exiting desktop mode interaction. */
+ public static final int CUJ_DESKTOP_MODE_EXIT_MODE = 108;
+
+ /** Track minimize window interaction in desktop mode. */
+ public static final int CUJ_DESKTOP_MODE_MINIMIZE_WINDOW = 109;
+
+ /** Track window drag interaction in desktop mode. */
+ public static final int CUJ_DESKTOP_MODE_DRAG_WINDOW = 110;
+
// When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
- @VisibleForTesting
- static final int LAST_CUJ = CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK;
+ @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_DRAG_WINDOW;
/** @hide */
@IntDef({
@@ -234,11 +268,17 @@
CUJ_LAUNCHER_WIDGET_PICKER_SEARCH_BACK,
CUJ_LAUNCHER_WIDGET_BOTTOM_SHEET_CLOSE_BACK,
CUJ_LAUNCHER_PRIVATE_SPACE_LOCK,
- CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK
+ CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK,
+ CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW,
+ CUJ_FOLD_ANIM,
+ CUJ_DESKTOP_MODE_RESIZE_WINDOW,
+ CUJ_DESKTOP_MODE_ENTER_MODE,
+ CUJ_DESKTOP_MODE_EXIT_MODE,
+ CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
+ CUJ_DESKTOP_MODE_DRAG_WINDOW
})
@Retention(RetentionPolicy.SOURCE)
- public @interface CujType {
- }
+ public @interface CujType {}
private static final int NO_STATSD_LOGGING = -1;
@@ -341,6 +381,13 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_WIDGET_EDU_SHEET_CLOSE_BACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_WIDGET_EDU_SHEET_CLOSE_BACK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_PRIVATE_SPACE_LOCK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_PRIVATE_SPACE_LOCK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_PRIVATE_SPACE_UNLOCK;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_MAXIMIZE_WINDOW;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_FOLD_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__FOLD_ANIM;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_RESIZE_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_RESIZE_WINDOW;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_ENTER_MODE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_ENTER_MODE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_EXIT_MODE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_EXIT_MODE;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_MINIMIZE_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_MINIMIZE_WINDOW;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_DRAG_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_DRAG_WINDOW;
}
private Cuj() {
@@ -543,6 +590,20 @@
return "LAUNCHER_PRIVATE_SPACE_LOCK";
case CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK:
return "LAUNCHER_PRIVATE_SPACE_UNLOCK";
+ case CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW:
+ return "DESKTOP_MODE_MAXIMIZE_WINDOW";
+ case CUJ_FOLD_ANIM:
+ return "FOLD_ANIM";
+ case CUJ_DESKTOP_MODE_RESIZE_WINDOW:
+ return "DESKTOP_MODE_RESIZE_WINDOW";
+ case CUJ_DESKTOP_MODE_ENTER_MODE:
+ return "DESKTOP_MODE_ENTER_MODE";
+ case CUJ_DESKTOP_MODE_EXIT_MODE:
+ return "DESKTOP_MODE_EXIT_MODE";
+ case CUJ_DESKTOP_MODE_MINIMIZE_WINDOW:
+ return "DESKTOP_MODE_MINIMIZE_WINDOW";
+ case CUJ_DESKTOP_MODE_DRAG_WINDOW:
+ return "DESKTOP_MODE_DRAG_WINDOW";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/jank/OWNERS b/core/java/com/android/internal/jank/OWNERS
index 2f3bbee..5463883 100644
--- a/core/java/com/android/internal/jank/OWNERS
+++ b/core/java/com/android/internal/jank/OWNERS
@@ -4,4 +4,5 @@
ahanwu@google.com
vadimt@google.com
marcinoc@google.com
-pmuetschard@google.com
\ No newline at end of file
+pmuetschard@google.com
+nicomazz@google.com
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 66b2a9c..238e6f5 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -75,10 +75,11 @@
/** @hide */
public class TransitionAnimation {
public static final int WALLPAPER_TRANSITION_NONE = 0;
- public static final int WALLPAPER_TRANSITION_OPEN = 1;
- public static final int WALLPAPER_TRANSITION_CLOSE = 2;
- public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 3;
- public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 4;
+ public static final int WALLPAPER_TRANSITION_CHANGE = 1;
+ public static final int WALLPAPER_TRANSITION_OPEN = 2;
+ public static final int WALLPAPER_TRANSITION_CLOSE = 3;
+ public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 4;
+ public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 5;
// These are the possible states for the enter/exit activities during a thumbnail transition
private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 13efaf0..754f77e7 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -28,6 +28,7 @@
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK;
@@ -227,7 +228,8 @@
public static final int ACTION_NOTIFICATION_BIG_PICTURE_LOADED = 23;
/**
- * Time it takes to unlock the device via udfps, until the whole launcher appears.
+ * Time it takes to unlock the device via fps,
+ * until either the launcher or the foreground app appears.
*/
public static final int ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME = 24;
@@ -249,6 +251,12 @@
*/
public static final int ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN = 27;
+ /**
+ * Time it takes to unlock the device via face,
+ * until either the launcher or the foreground app appears.
+ */
+ public static final int ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME = 28;
+
private static final int[] ACTIONS_ALL = {
ACTION_EXPAND_PANEL,
ACTION_TOGGLE_RECENTS,
@@ -278,6 +286,7 @@
ACTION_BACK_SYSTEM_ANIMATION,
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
+ ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME,
};
/** @hide */
@@ -310,6 +319,7 @@
ACTION_BACK_SYSTEM_ANIMATION,
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
+ ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Action {
@@ -345,6 +355,7 @@
UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME,
};
private final Object mLock = new Object();
@@ -539,6 +550,8 @@
return "ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE";
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN:
return "ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE_WITH_SHADE_OPEN";
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME:
+ return "ACTION_KEYGUARD_FACE_UNLOCK_TO_HOME";
default:
throw new IllegalArgumentException("Invalid action");
}
diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
index 507b673..3fd5120 100644
--- a/core/java/com/android/internal/view/SurfaceCallbackHelper.java
+++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
@@ -16,11 +16,13 @@
package com.android.internal.view;
-import android.os.RemoteException;
-import android.view.Surface;
+import android.util.EventLog;
import android.view.SurfaceHolder;
public class SurfaceCallbackHelper {
+ private static final int LOGTAG_SURFACEVIEW_CALLBACK = 60006;
+ private final String mTag;
+ private boolean mSurfaceRedrawImplemented;
Runnable mRunnable;
int mFinishDrawingCollected = 0;
@@ -35,12 +37,23 @@
return;
}
mRunnable.run();
+ if (mSurfaceRedrawImplemented && mTag != null) {
+ EventLog.writeEvent(LOGTAG_SURFACEVIEW_CALLBACK, mTag,
+ "surfaceRedrawNeeded implemented");
+ }
}
}
};
public SurfaceCallbackHelper(Runnable callbacksCollected) {
+ // skip logging surfaceRedrawNeeded calls
+ this(callbacksCollected, null);
+ }
+
+ public SurfaceCallbackHelper(Runnable callbacksCollected, String tag) {
mRunnable = callbacksCollected;
+ mTag = tag;
+ mSurfaceRedrawImplemented = false;
}
public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) {
@@ -58,6 +71,7 @@
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2) c).surfaceRedrawNeededAsync(
holder, mFinishDrawingRunnable);
+ mSurfaceRedrawImplemented = true;
} else {
mFinishDrawingRunnable.run();
}
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 1d0e972..7e8724d 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -39,6 +39,6 @@
boolean hasStableIds();
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean isCreated();
- RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize);
+ RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize, int capBitmapSize);
}
diff --git a/core/java/com/android/internal/widget/NotificationCloseButton.java b/core/java/com/android/internal/widget/NotificationCloseButton.java
new file mode 100644
index 0000000..bce266d
--- /dev/null
+++ b/core/java/com/android/internal/widget/NotificationCloseButton.java
@@ -0,0 +1,103 @@
+/*
+ * 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.widget;
+
+import android.annotation.ColorInt;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+import com.android.internal.R;
+
+/**
+ * A close button in a notification
+ */
+@RemoteViews.RemoteView
+public class NotificationCloseButton extends ImageView {
+
+ @ColorInt private int mBackgroundColor;
+ @ColorInt private int mForegroundColor;
+
+ public NotificationCloseButton(Context context) {
+ this(context, null, 0, 0);
+ }
+
+ public NotificationCloseButton(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0, 0);
+ }
+
+ public NotificationCloseButton(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public NotificationCloseButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setContentDescription(mContext.getText(R.string.close_button_text));
+ boolean notificationCloseButtonSupported = Resources.getSystem().getBoolean(
+ com.android.internal.R.bool.config_notificationCloseButtonSupported);
+ this.setVisibility(notificationCloseButtonSupported ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Button.class.getName());
+ }
+
+
+ private void updateColors() {
+ if (mBackgroundColor != 0) {
+ this.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
+ }
+ if (mForegroundColor != 0) {
+ this.setImageTintList(ColorStateList.valueOf(mForegroundColor));
+ }
+ }
+
+ /**
+ * Set the color used for the foreground.
+ */
+ @RemotableViewMethod
+ public void setForegroundColor(@ColorInt int color) {
+ mForegroundColor = color;
+ updateColors();
+ }
+
+ /**
+ * Sets the color used for the background.
+ */
+ @RemotableViewMethod
+ public void setBackgroundColor(@ColorInt int color) {
+ mBackgroundColor = color;
+ updateColors();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 00262be..5c2a167 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core;
+import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.Theme;
@@ -308,9 +309,10 @@
/**
* Returns true if x,y coordinate is within bounds
+ *
* @param x x-coordinate
* @param y y-coordinate
- * @return x,y coordinate is within bounds
+ * @return x, y coordinate is within bounds
*/
public boolean contains(float x, float y) {
return x >= mLeft && x < mRight
@@ -483,6 +485,37 @@
return builder.toString();
}
+ /**
+ * Gets the names of all named colors.
+ *
+ * @return array of named colors or null
+ */
+ public String[] getNamedColors() {
+ int count = 0;
+ for (Operation op : mOperations) {
+ if (op instanceof NamedVariable) {
+ NamedVariable n = (NamedVariable) op;
+ if (n.mVarType == NamedVariable.COLOR_TYPE) {
+ count++;
+ }
+ }
+ }
+ if (count == 0) {
+ return null;
+ }
+ String[] ret = new String[count];
+ int i = 0;
+ for (Operation op : mOperations) {
+ if (op instanceof NamedVariable) {
+ NamedVariable n = (NamedVariable) op;
+ if (n.mVarType == NamedVariable.COLOR_TYPE) {
+ ret[i++] = n.mVarName;
+ }
+ }
+ }
+ return ret;
+ }
+
//////////////////////////////////////////////////////////////////////////
// Painting
//////////////////////////////////////////////////////////////////////////
@@ -493,6 +526,7 @@
/**
* Returns > 0 if it needs to repaint
+ *
* @return
*/
public int needsRepaint() {
@@ -525,7 +559,6 @@
context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, getWidth());
context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, getHeight());
mRepaintNext = context.updateOps();
-
for (Operation op : mOperations) {
// operations will only be executed if no theme is set (ie UNSPECIFIED)
// or the theme is equal as the one passed in argument to paint.
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 4b45ab6..fc8668e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -19,6 +19,7 @@
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorConstant;
import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
@@ -42,6 +43,7 @@
import com.android.internal.widget.remotecompose.core.operations.MatrixScale;
import com.android.internal.widget.remotecompose.core.operations.MatrixSkew;
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
+import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.PaintData;
import com.android.internal.widget.remotecompose.core.operations.PathData;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
@@ -105,6 +107,8 @@
public static final int COLOR_EXPRESSIONS = 134;
public static final int TEXT_FROM_FLOAT = 135;
public static final int TEXT_MERGE = 136;
+ public static final int NAMED_VARIABLE = 137;
+ public static final int COLOR_CONSTANT = 138;
/////////////////////////////////////////======================
public static IntMap<CompanionOperation> map = new IntMap<>();
@@ -147,7 +151,8 @@
map.put(COLOR_EXPRESSIONS, ColorExpression.COMPANION);
map.put(TEXT_FROM_FLOAT, TextFromFloat.COMPANION);
map.put(TEXT_MERGE, TextMerge.COMPANION);
-
+ map.put(NAMED_VARIABLE, NamedVariable.COMPANION);
+ map.put(COLOR_CONSTANT, ColorConstant.COMPANION);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 52fc314..d462c7d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -19,6 +19,7 @@
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorConstant;
import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
@@ -42,6 +43,7 @@
import com.android.internal.widget.remotecompose.core.operations.MatrixScale;
import com.android.internal.widget.remotecompose.core.operations.MatrixSkew;
import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
+import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.PaintData;
import com.android.internal.widget.remotecompose.core.operations.PathData;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
@@ -899,6 +901,20 @@
}
/**
+ * Add a simple color
+ * @param color
+ * @return id that represents that color
+ */
+ public int addColor(int color) {
+ ColorConstant c = new ColorConstant(0, color);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mColorId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+
+ /**
* Add a color that represents the tween between two colors
* @param color1
* @param color2
@@ -1013,5 +1029,14 @@
return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
}
+ /**
+ * This defines the name of the color given the id.
+ * @param id of the color
+ * @param name Name of the color
+ */
+ public void setColorName(int id, String name) {
+ NamedVariable.COMPANION.apply(mBuffer, id,
+ NamedVariable.COLOR_TYPE, name);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 66a37e67..bfe67c8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -33,11 +33,13 @@
public class RemoteComposeState {
public static final int START_ID = 42;
private static final int MAX_FLOATS = 500;
+ private static final int MAX_COLORS = 200;
private final IntMap<Object> mIntDataMap = new IntMap<>();
private final IntMap<Boolean> mIntWrittenMap = new IntMap<>();
private final HashMap<Object, Integer> mDataIntMap = new HashMap();
private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache
- private final int[] mColorMap = new int[MAX_FLOATS]; // efficient cache
+ private final int[] mColorMap = new int[MAX_COLORS]; // efficient cache
+ private final boolean[] mColorOverride = new boolean[MAX_COLORS];
private int mNextId = START_ID;
{
@@ -49,6 +51,7 @@
/**
* Get Object based on id. The system will cache things like bitmaps
* Paths etc. They can be accessed with this command
+ *
* @param id
* @return
*/
@@ -58,6 +61,7 @@
/**
* true if the cache contain this id
+ *
* @param id
* @return
*/
@@ -150,9 +154,32 @@
* @param color
*/
public void updateColor(int id, int color) {
+ if (mColorOverride[id]) {
+ return;
+ }
mColorMap[id] = color;
}
+ /**
+ * Adds a colorOverride.
+ * This is a list of ids and there colors optimized for playback;
+ *
+ * @param id
+ * @param color
+ */
+ public void overrideColor(int id, int color) {
+ mColorOverride[id] = true;
+ mColorMap[id] = color;
+ }
+
+ /**
+ * Clear the color Overrides
+ */
+ public void clearColorOverride() {
+ for (int i = 0; i < mColorOverride.length; i++) {
+ mColorOverride[i] = false;
+ }
+ }
/**
* Method to determine if a cached value has been written to the documents WireBuffer based on
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 7e72168..32027d8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -72,6 +72,14 @@
return (System.nanoTime() - mStart) * 1E-9f;
}
+ /**
+ * Set the value of a named Color.
+ * This overrides the color in the document
+ * @param colorName
+ * @param color
+ */
+ public abstract void setNamedColorOverride(String colorName, int color);
+
/**
* The context can be used in a few different mode, allowing operations to skip being executed:
@@ -262,16 +270,45 @@
public static final int ID_COMPONENT_WIDTH = 7;
public static final int ID_COMPONENT_HEIGHT = 8;
public static final int ID_CALENDAR_MONTH = 9;
+ public static final int ID_OFFSET_TO_UTC = 10;
+ public static final int ID_WEEK_DAY = 11;
+ public static final int ID_DAY_OF_MONTH = 12;
+ /**
+ * CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
+ */
public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
+ /**
+ * seconds run from Midnight=0 quantized to seconds hour 0..3599
+ */
public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC);
+ /**
+ * minutes run from Midnight=0 quantized to minutes 0..1439
+ */
public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN);
+ /**
+ * hours run from Midnight=0 quantized to Hours 0-23
+ */
public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR);
+ /**
+ * Moth of Year quantized to MONTHS 1-12. 1 = January
+ */
public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH);
+ /**
+ * DAY OF THE WEEK 1-7. 1 = Monday
+ */
+ public static final float FLOAT_WEEK_DAY = Utils.asNan(ID_WEEK_DAY);
+ /**
+ * DAY OF THE MONTH 1-31
+ */
+ public static final float FLOAT_DAY_OF_MONTH = Utils.asNan(ID_DAY_OF_MONTH);
+
public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH);
public static final float FLOAT_WINDOW_HEIGHT = Utils.asNan(ID_WINDOW_HEIGHT);
public static final float FLOAT_COMPONENT_WIDTH = Utils.asNan(ID_COMPONENT_WIDTH);
public static final float FLOAT_COMPONENT_HEIGHT = Utils.asNan(ID_COMPONENT_HEIGHT);
+ // ID_OFFSET_TO_UTC is the offset from UTC in sec (typically / 3600f)
+ public static final float FLOAT_OFFSET_TO_UTC = Utils.asNan(ID_OFFSET_TO_UTC);
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index e9708b7..04e04bbb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -16,6 +16,9 @@
package com.android.internal.widget.remotecompose.core;
import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
/**
* This generates the standard system variables for time.
@@ -23,6 +26,7 @@
public class TimeVariables {
/**
* This class populates all time variables in the system
+ *
* @param context
*/
public void updateTime(RemoteContext context) {
@@ -33,19 +37,29 @@
// hours run from Midnight=0 quantized to Hours 0-23
// CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
// CONTINUOUS_SEC is accurate to milliseconds due to float precession
- int month = dateTime.getDayOfMonth();
+ // ID_OFFSET_TO_UTC is the offset from UTC in sec (typically / 3600f)
+ int month = dateTime.getMonth().getValue();
int hour = dateTime.getHour();
int minute = dateTime.getMinute();
int seconds = dateTime.getSecond();
int currentMinute = hour * 60 + minute;
int currentSeconds = minute * 60 + seconds;
float sec = currentSeconds + dateTime.getNano() * 1E-9f;
+ int day_week = dateTime.getDayOfWeek().getValue();
+
+ ZoneId zone = ZoneId.systemDefault();
+ OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime();
+ ZoneOffset offset = offsetDateTime.getOffset();
+
+ context.loadFloat(RemoteContext.ID_OFFSET_TO_UTC, offset.getTotalSeconds());
context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec);
context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds);
context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute);
context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour);
context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
+ context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month);
+ context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
new file mode 100644
index 0000000..15c208f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
@@ -0,0 +1,94 @@
+/*
+ * 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.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation that defines a simple Color based on ID
+ * Mainly for colors in theming.
+ */
+public class ColorConstant implements Operation {
+ public int mColorId;
+ public int mColor;
+ public static final Companion COMPANION = new Companion();
+
+ public ColorConstant(int colorId, int color) {
+ this.mColorId = colorId;
+ this.mColor = color;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mColorId, mColor);
+ }
+
+ @Override
+ public String toString() {
+ return "ColorConstant[" + mColorId + "] = " + Utils.colorInt(mColor) + "";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "ColorConstant";
+ }
+
+ @Override
+ public int id() {
+ return Operations.COLOR_CONSTANT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer
+ * @param colorId
+ * @param color
+ */
+ public void apply(WireBuffer buffer, int colorId, int color) {
+ buffer.start(Operations.COLOR_CONSTANT);
+ buffer.writeInt(colorId);
+ buffer.writeInt(color);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int colorId = buffer.readInt();
+ int color = buffer.readInt();
+ operations.add(new ColorConstant(colorId, color));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadColor(mColorId, mColor);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
index 0c5b286..ae27f5f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -32,7 +32,9 @@
public int mVarType;
public static final Companion COMPANION = new Companion();
public static final int MAX_STRING_SIZE = 4000;
-
+ public static final int COLOR_TYPE = 2;
+ public static final int FLOAT_TYPE = 1;
+ public static final int STRING_TYPE = 0;
public NamedVariable(int varId, int varType, String name) {
this.mVarId = varId;
this.mVarType = varType;
@@ -72,7 +74,7 @@
* @param text
*/
public void apply(WireBuffer buffer, int varId, int varType, String text) {
- buffer.start(Operations.DATA_TEXT);
+ buffer.start(Operations.NAMED_VARIABLE);
buffer.writeInt(varId);
buffer.writeInt(varType);
buffer.writeUTF8(text);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index fdc6860..fcb3bfa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -78,7 +78,9 @@
*/
public static void log(String str) {
StackTraceElement s = new Throwable().getStackTrace()[1];
- System.out.println("(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str);
+ System.out.println("(" + s.getFileName()
+ + ":" + s.getLineNumber() + "). "
+ + s.getMethodName() + "() " + str);
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index d1c4d46..a42c584 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -103,5 +103,15 @@
return "Document{\n"
+ mDocument + '}';
}
+
+ /**
+ * Gets a array of Names of the named colors defined in the loaded doc.
+ *
+ * @return
+ */
+ public String[] getNamedColors() {
+ return mDocument.getNamedColors();
+ }
+
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 7423a16..73e94fa 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -16,9 +16,11 @@
package com.android.internal.widget.remotecompose.player;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
@@ -53,6 +55,7 @@
/**
* Turn on debug information
+ *
* @param debugFlags 1 to set debug on
*/
public void setDebug(int debugFlags) {
@@ -79,6 +82,7 @@
} else {
mInner.setDocument(null);
}
+ mapColors();
}
/**
@@ -106,7 +110,8 @@
LayoutParams.MATCH_PARENT);
addView(horizontalScrollView, layoutParams);
}
- } break;
+ }
+ break;
case RootContentBehavior.SCROLL_VERTICAL: {
if (!(mInner.getParent() instanceof ScrollView)) {
((ViewGroup) mInner.getParent()).removeView(mInner);
@@ -123,9 +128,10 @@
LayoutParams.MATCH_PARENT);
addView(scrollView, layoutParams);
}
- } break;
+ }
+ break;
default:
- if (mInner.getParent() != this) {
+ if (mInner.getParent() != this) {
((ViewGroup) mInner.getParent()).removeView(mInner);
removeAllViews();
LayoutParams layoutParams = new LayoutParams(
@@ -178,5 +184,230 @@
mInner.invalidate();
}
}
+
+ /**
+ * This returns a list of colors that have names in the Document.
+ *
+ * @return
+ */
+ public String[] getNamedColors() {
+ return mInner.getNamedColors();
+ }
+
+ /**
+ * This sets a color based on its name. Overriding the color set in
+ * the document.
+ *
+ * @param colorName Name of the color
+ * @param colorValue The new color value
+ */
+ public void setColor(String colorName, int colorValue) {
+ mInner.setColor(colorName, colorValue);
+ }
+
+ private void mapColors() {
+ String[] name = getNamedColors();
+
+ // make every effort to terminate early
+ if (name == null) {
+ return;
+ }
+ boolean found = false;
+ for (int i = 0; i < name.length; i++) {
+ if (name[i].startsWith("android.")) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+
+ for (int i = 0; i < name.length; i++) {
+ String s = name[i];
+ if (!s.startsWith("android.")) {
+ continue;
+ }
+ String sub = s.substring("android.".length());
+ switch (sub) {
+ case "actionBarItemBackground":
+ setRColor(s, android.R.attr.actionBarItemBackground);
+ break;
+ case "actionModeBackground":
+ setRColor(s, android.R.attr.actionModeBackground);
+ break;
+ case "actionModeSplitBackground":
+ setRColor(s, android.R.attr.actionModeSplitBackground);
+ break;
+ case "activatedBackgroundIndicator":
+ setRColor(s, android.R.attr.activatedBackgroundIndicator);
+ break;
+ case "colorAccent": // Highlight color for interactive elements
+ setRColor(s, android.R.attr.colorAccent);
+ break;
+ case "colorActivatedHighlight":
+ setRColor(s, android.R.attr.colorActivatedHighlight);
+ break;
+ case "colorBackground": // background color for the app’s window
+ setRColor(s, android.R.attr.colorBackground);
+ break;
+ case "colorBackgroundCacheHint":
+ setRColor(s, android.R.attr.colorBackgroundCacheHint);
+ break;
+ // Background color for floating elements
+ case "colorBackgroundFloating":
+ setRColor(s, android.R.attr.colorBackgroundFloating);
+ break;
+ case "colorButtonNormal": // The default color for buttons
+ setRColor(s, android.R.attr.colorButtonNormal);
+ break;
+ // Color for activated (checked) state of controls.
+ case "colorControlActivated":
+ setRColor(s, android.R.attr.colorControlActivated);
+ break;
+ case "colorControlHighlight": // Color for highlights on controls
+ setRColor(s, android.R.attr.colorControlHighlight);
+ break;
+ // Default color for controls in their normal state.
+ case "colorControlNormal":
+ setRColor(s, android.R.attr.colorControlNormal);
+ break;
+ // Color for edge effects (e.g., overscroll glow)
+ case "colorEdgeEffect":
+ setRColor(s, android.R.attr.colorEdgeEffect);
+ break;
+ case "colorError":
+ setRColor(s, android.R.attr.colorError);
+ break;
+ case "colorFocusedHighlight":
+ setRColor(s, android.R.attr.colorFocusedHighlight);
+ break;
+ case "colorForeground": // General foreground color for views.
+ setRColor(s, android.R.attr.colorForeground);
+ break;
+ // Foreground color for inverse backgrounds.
+ case "colorForegroundInverse":
+ setRColor(s, android.R.attr.colorForegroundInverse);
+ break;
+ case "colorLongPressedHighlight":
+ setRColor(s, android.R.attr.colorLongPressedHighlight);
+ break;
+ case "colorMultiSelectHighlight":
+ setRColor(s, android.R.attr.colorMultiSelectHighlight);
+ break;
+ case "colorPressedHighlight":
+ setRColor(s, android.R.attr.colorPressedHighlight);
+ break;
+ case "colorPrimary": // The primary branding color for the app.
+ setRColor(s, android.R.attr.colorPrimary);
+ break;
+ case "colorPrimaryDark": // darker variant of the primary color
+ setRColor(s, android.R.attr.colorPrimaryDark);
+ break;
+ case "colorSecondary":
+ setRColor(s, android.R.attr.colorSecondary);
+ break;
+ case "detailsElementBackground":
+ setRColor(s, android.R.attr.detailsElementBackground);
+ break;
+ case "editTextBackground":
+ setRColor(s, android.R.attr.editTextBackground);
+ break;
+ case "galleryItemBackground":
+ setRColor(s, android.R.attr.galleryItemBackground);
+ break;
+ case "headerBackground":
+ setRColor(s, android.R.attr.headerBackground);
+ break;
+ case "itemBackground":
+ setRColor(s, android.R.attr.itemBackground);
+ break;
+ case "numbersBackgroundColor":
+ setRColor(s, android.R.attr.numbersBackgroundColor);
+ break;
+ case "panelBackground":
+ setRColor(s, android.R.attr.panelBackground);
+ break;
+ case "panelColorBackground":
+ setRColor(s, android.R.attr.panelColorBackground);
+ break;
+ case "panelFullBackground":
+ setRColor(s, android.R.attr.panelFullBackground);
+ break;
+ case "popupBackground":
+ setRColor(s, android.R.attr.popupBackground);
+ break;
+ case "queryBackground":
+ setRColor(s, android.R.attr.queryBackground);
+ break;
+ case "selectableItemBackground":
+ setRColor(s, android.R.attr.selectableItemBackground);
+ break;
+ case "submitBackground":
+ setRColor(s, android.R.attr.submitBackground);
+ break;
+ case "textColor":
+ setRColor(s, android.R.attr.textColor);
+ break;
+ case "windowBackground":
+ setRColor(s, android.R.attr.windowBackground);
+ break;
+ case "windowBackgroundFallback":
+ setRColor(s, android.R.attr.windowBackgroundFallback);
+ break;
+ // Primary text color for inverse backgrounds
+ case "textColorPrimaryInverse":
+ setRColor(s, android.R.attr.textColorPrimaryInverse);
+ break;
+ // Secondary text color for inverse backgrounds
+ case "textColorSecondaryInverse":
+ setRColor(s, android.R.attr.textColorSecondaryInverse);
+ break;
+ // Tertiary text color for less important text.
+ case "textColorTertiary":
+ setRColor(s, android.R.attr.textColorTertiary);
+ break;
+ // Tertiary text color for inverse backgrounds
+ case "textColorTertiaryInverse":
+ setRColor(s, android.R.attr.textColorTertiaryInverse);
+ break;
+ // Text highlight color (e.g., selected text background).
+ case "textColorHighlight":
+ setRColor(s, android.R.attr.textColorHighlight);
+ break;
+ // Color for hyperlinks.
+ case "textColorLink":
+ setRColor(s, android.R.attr.textColorLink);
+ break;
+ // Color for hint text.
+ case "textColorHint":
+ setRColor(s, android.R.attr.textColorHint);
+ break;
+ // text color for inverse backgrounds..
+ case "textColorHintInverse":
+ setRColor(s, android.R.attr.textColorHintInverse);
+ break;
+ // Default color for the thumb of switches.
+ case "colorSwitchThumbNormal":
+ setRColor(s, android.R.attr.colorControlNormal);
+ break;
+ }
+ }
+ }
+
+ private void setRColor(String name, int id) {
+ int color = getColorFromResource(id);
+ setColor(name, color);
+ }
+
+ private int getColorFromResource(int id) {
+ TypedValue typedValue = new TypedValue();
+ try (TypedArray arr = getContext()
+ .getApplicationContext()
+ .obtainStyledAttributes(typedValue.data, new int[]{id})) {
+ int color = arr.getColor(0, -1);
+ return color;
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 6e4893b..dd43bd5 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -76,6 +76,16 @@
}
/**
+ * Override a color to force it to be the color provided
+ *
+ * @param colorName name of color
+ * @param color
+ */
+ public void setNamedColorOverride(String colorName, int color) {
+ int id = mVarNameHashMap.get(colorName).mId;
+ mRemoteComposeState.overrideColor(id, color);
+ }
+ /**
* Decode a byte array into an image and cache it using the given imageId
*
* @param width with of image to be loaded
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 97d23c8..a2f79cc 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -42,6 +42,7 @@
boolean mInActionDown = false;
boolean mDebug = false;
Point mActionDownPoint = new Point(0, 0);
+ AndroidRemoteContext mARContext = new AndroidRemoteContext();
public RemoteComposeCanvas(Context context) {
super(context);
@@ -88,8 +89,6 @@
invalidate();
}
- AndroidRemoteContext mARContext = new AndroidRemoteContext();
-
@Override
public void onViewAttachedToWindow(View view) {
if (mDocument == null) {
@@ -120,6 +119,20 @@
removeAllViews();
}
+ public String[] getNamedColors() {
+ return mDocument.getNamedColors();
+ }
+
+ /**
+ * set the color associated with this name.
+ *
+ * @param colorName Name of color typically "android.xxx"
+ * @param colorValue "the argb value"
+ */
+ public void setColor(String colorName, int colorValue) {
+ mARContext.setNamedColorOverride(colorName, colorValue);
+ }
+
public interface ClickCallbacks {
void click(int id, String metadata);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 61eaa52..011ef30 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -111,6 +111,7 @@
"libminikin",
"libz",
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
"android.database.sqlite-aconfig-cc",
"android.media.audiopolicy-aconfig-cc",
],
@@ -127,6 +128,7 @@
],
defaults: [
+ "aconfig_lib_cc_shared_link.defaults",
"latest_android_media_audio_common_types_cpp_target_shared",
],
@@ -194,6 +196,7 @@
"android_os_PerformanceHintManager.cpp",
"android_os_SELinux.cpp",
"android_os_ServiceManager.cpp",
+ "android_os_ServiceManagerNative.cpp",
"android_os_SharedMemory.cpp",
"android_os_storage_StorageManager.cpp",
"android_os_UEventObserver.cpp",
@@ -365,6 +368,7 @@
"libdl_android",
"libtimeinstate",
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
"libimage_io",
"libultrahdr",
"libperfetto_c",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 71d041c..ed59327 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -148,6 +148,7 @@
extern int register_android_os_HwRemoteBinder(JNIEnv *env);
extern int register_android_os_NativeHandle(JNIEnv *env);
extern int register_android_os_ServiceManager(JNIEnv *env);
+extern int register_android_os_ServiceManagerNative(JNIEnv* env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_PerformanceHintManager(JNIEnv* env);
@@ -1544,6 +1545,7 @@
REG_JNI(register_android_os_HwRemoteBinder),
REG_JNI(register_android_os_NativeHandle),
REG_JNI(register_android_os_ServiceManager),
+ REG_JNI(register_android_os_ServiceManagerNative),
REG_JNI(register_android_os_storage_StorageManager),
REG_JNI(register_android_service_DataLoaderService),
REG_JNI(register_android_view_DisplayEventReceiver),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 163f32e..b2bc19c 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -2,7 +2,7 @@
per-file *Camera*,*camera* = file:platform/frameworks/av:/camera/OWNERS
# Connectivity
-per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
+per-file android_net_* = jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
# Choreographer
per-file android_view_DisplayEventReceiver* = file:platform/frameworks/native:/services/surfaceflinger/OWNERS
@@ -84,7 +84,7 @@
# These are highly common-use files
per-file Android.bp = file:/graphics/java/android/graphics/OWNERS
per-file AndroidRuntime.cpp = file:/graphics/java/android/graphics/OWNERS
-per-file AndroidRuntime.cpp = calin@google.com, ngeoffray@google.com, oth@google.com
+per-file AndroidRuntime.cpp = file:platform/art:main:/OWNERS
# Although marked "view" this is mostly graphics stuff
per-file android_view_* = file:/graphics/java/android/graphics/OWNERS
# File used for Android Studio layoutlib
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 8f70268..7410468 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -134,10 +134,15 @@
String8 label(labelChars);
env->ReleaseStringUTFChars(labelStr, labelChars);
+ errno = 0;
sqlite3* db;
int err = sqlite3_open_v2(path.c_str(), &db, sqliteFlags, NULL);
if (err != SQLITE_OK) {
- throw_sqlite3_exception_errcode(env, err, "Could not open database");
+ if (errno == 0) {
+ throw_sqlite3_exception(env, db, "could not open database");
+ } else {
+ throw_sqlite3_exception(env, db, strerror(errno));
+ }
return 0;
}
diff --git a/core/jni/android_os_ServiceManagerNative.cpp b/core/jni/android_os_ServiceManagerNative.cpp
new file mode 100644
index 0000000..39a3013
--- /dev/null
+++ b/core/jni/android_os_ServiceManagerNative.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <binder/IServiceManagerFFI.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "android_util_Binder.h"
+namespace android {
+
+jobject JNICALL Java_android_os_ServiceManagerProxy_getNativeServiceManager(JNIEnv *env,
+ jobject obj) {
+ sp<IBinder> service = IInterface::asBinder(
+ impl::getJavaServicemanagerImplPrivateDoNotUseExceptInTheOnePlaceItIsUsed());
+ return javaObjectForIBinder(env, service);
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod serviceManagerNativeMethods[] = {
+ /* name, signature, funcPtr */
+ {"getNativeServiceManager", "()Landroid/os/IBinder;",
+ (void *)Java_android_os_ServiceManagerProxy_getNativeServiceManager}};
+
+int register_android_os_ServiceManagerNative(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, "android/os/ServiceManagerProxy",
+ serviceManagerNativeMethods,
+ NELEM(serviceManagerNativeMethods));
+}
+} // namespace android
\ No newline at end of file
diff --git a/core/proto/android/content/res/color_state_list.proto b/core/proto/android/content/res/color_state_list.proto
new file mode 100644
index 0000000..3d0d8a8
--- /dev/null
+++ b/core/proto/android/content/res/color_state_list.proto
@@ -0,0 +1,36 @@
+/*
+ * 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 optional 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.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+
+package android.content.res;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+/**
+ * An android.content.res.ColorStateList object.
+ */
+message ColorStateListProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ repeated StateSpec state_specs = 1;
+ repeated int32 colors = 2 [packed = true];
+
+ message StateSpec {
+ repeated int32 state = 1 [packed = true];
+ }
+}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 921c41c..90069f1 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -1038,7 +1038,7 @@
optional int32 uid = 1;
repeated .android.app.ApplicationExitInfoProto app_exit_info = 2;
- repeated .android.app.ApplicationExitInfoProto app_recoverable_crash = 3;
+ repeated .android.app.ApplicationExitInfoProto app_recoverable_crash = 3 [deprecated=true];
}
repeated User users = 2;
}
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 1d9b0db..5a4d6db 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -146,6 +146,7 @@
IGNORED_FROM_VIRTUAL_DEVICE = 26;
IGNORED_ON_WIRELESS_CHARGER = 27;
IGNORED_MISSING_PERMISSION = 28;
+ CANCELLED_BY_APP_OPS = 29;
reserved 17; // prev IGNORED_UNKNOWN_VIBRATION
}
}
diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto
new file mode 100644
index 0000000..f08ea1b
--- /dev/null
+++ b/core/proto/android/widget/remoteviews.proto
@@ -0,0 +1,80 @@
+/*
+ * 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 optional 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.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+
+package android.widget;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+/**
+ * An android.widget.RemoteViews object. This is used by RemoteViews.createPreviewFromProto
+ * and RemoteViews.writePreviewToProto.
+ *
+ * Any addition of fields here will require an update to the parsing code in RemoteViews.java.
+ * Otherwise the field will be ignored when parsing (with a logged warning).
+ *
+ * Do not change the tag number or type of any fields in order to maintain compatibility with
+ * previous versions. If a field is deleted, use `reserved` to mark its tag number.
+ */
+message RemoteViewsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int32 mode = 1;
+ optional string package_name = 2;
+ optional string layout_id = 3;
+ optional string light_background_layout_id = 4;
+ optional string view_id = 5;
+ optional SizeFProto ideal_size = 6;
+ optional int32 apply_flags = 7;
+ optional int64 provider_instance_id = 8;
+ // RemoteViews for different sizes (created with RemoteViews(Map<SizeF, RemoteViews)
+ // constructor).
+ repeated RemoteViewsProto sized_remoteviews = 9;
+ // RemoteViews for portrait/landscape (created with RemoteViews(RemoteViews, RemoteViews)i
+ // constructor).
+ optional RemoteViewsProto portrait_remoteviews = 10;
+ optional RemoteViewsProto landscape_remoteviews = 11;
+ optional bool is_root = 12;
+ optional bool has_draw_instructions = 13;
+ repeated bytes bitmap_cache = 14;
+ optional RemoteCollectionCache remote_collection_cache = 15;
+
+ message RemoteCollectionCache {
+ message Entry {
+ optional int64 id = 1;
+ optional string uri = 2;
+ optional RemoteCollectionItems items = 3;
+ }
+
+ repeated Entry entries = 1;
+ }
+
+ message RemoteCollectionItems {
+ repeated int64 ids = 1 [packed = true];
+ repeated RemoteViewsProto views = 2;
+ optional bool has_stable_ids = 3;
+ optional int32 view_type_count = 4;
+ optional bool attached = 5;
+ }
+}
+
+
+message SizeFProto {
+ optional float width = 1;
+ optional float height = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c71f9bd..f61b6bf 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5593,6 +5593,15 @@
<permission android:name="android.permission.BIND_TV_INPUT"
android:protectionLevel="signature|privileged" />
+ <!-- This permission is required among systems services to always keep the
+ binding with TvInputManagerService.
+ <p>This should only be used by the OEM TvInputService.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ @hide
+ -->
+ <permission android:name="android.permission.ALWAYS_BOUND_TV_INPUT"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
<!-- Must be required by a {@link android.media.tv.interactive.TvInteractiveAppService}
to ensure that only the system can bind to it.
<p>Protection level: signature|privileged
@@ -8222,6 +8231,19 @@
<permission android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD"
android:protectionLevel="internal|appop" />
+ <!-- @hide @SystemApi
+ @FlaggedApi(android.os.Flags.FLAG_ALLOW_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT)
+ Allows system applications to capture bugreport directly without consent dialog when using
+ the bugreporting API, and instead use the applications-owned consent page.
+ <p>The application still needs to hold {@link android.permission.DUMP} permission and be
+ bugreport-whitelisted to be able to capture a bugreport using the bugreporting API in the
+ first place.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications. -->
+ <permission
+ android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows to call APIs that log process lifecycle events
@hide -->
<permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE"
diff --git a/core/res/res/drawable/notification_close_button_icon.xml b/core/res/res/drawable/notification_close_button_icon.xml
new file mode 100644
index 0000000..947cd5a
--- /dev/null
+++ b/core/res/res/drawable/notification_close_button_icon.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/notification_close_button_size"
+ android:height="@dimen/notification_close_button_size"
+ android:viewportWidth="16.0"
+ android:viewportHeight="16.0">
+<path
+ android:fillColor="#FF000000"
+ android:pathData="M 12.6667 4.2733 L 11.7267 3.3333 L 8 7.06 L 4.2734 3.3333 L 3.3334
+4.2733 L 7.06 8 L 3.3334 11.7267 L 4.2734 12.6667 L 8 8.94 L 11.7267 12.6667 L 12.6667
+11.7267 L 8.94 8 L 12.6667 4.2733 Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml
index 2f97bae..f50af15 100644
--- a/core/res/res/layout/accessibility_button_chooser.xml
+++ b/core/res/res/layout/accessibility_button_chooser.xml
@@ -47,6 +47,16 @@
android:paddingTop="8dp"
android:paddingBottom="8dp"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/accessibility_button_prompt"
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:text="@string/accessibility_button_instructional_text"
+ android:gravity="start|center_vertical"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/>
+
<GridView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -57,16 +67,5 @@
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"/>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/accessibility_button_prompt"
- android:textAppearance="?attr/textAppearanceMedium"
- android:text="@string/accessibility_button_instructional_text"
- android:gravity="start|center_vertical"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:visibility="gone"/>
</LinearLayout>
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/notification_close_button.xml b/core/res/res/layout/notification_close_button.xml
new file mode 100644
index 0000000..5eff84e
--- /dev/null
+++ b/core/res/res/layout/notification_close_button.xml
@@ -0,0 +1,30 @@
+<?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.internal.widget.NotificationCloseButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:contentDescription="@string/close_button_text"
+ android:visibility="gone"
+ android:src="@drawable/notification_close_button_icon"
+ android:padding="2dp"
+ android:scaleType="fitCenter"
+ android:importantForAccessibility="no"
+ >
+</com.android.internal.widget.NotificationCloseButton>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index d80b765..e44c727 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -83,11 +83,28 @@
android:focusable="false"
/>
- <include layout="@layout/notification_expand_button"
+ <LinearLayout
+ android:id="@+id/notification_buttons_column"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- />
+ android:orientation="vertical"
+ >
+
+ <include layout="@layout/notification_close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="end"
+ android:layout_marginEnd="20dp"
+ />
+
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ />
+
+ </LinearLayout>
</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 452df50..29f14a4 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -157,20 +157,38 @@
android:maxDrawableHeight="@dimen/notification_right_icon_size"
/>
- <FrameLayout
- android:id="@+id/expand_button_touch_container"
+ <LinearLayout
+ android:id="@+id/notification_buttons_column"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
+ android:layout_alignParentEnd="true"
+ android:orientation="vertical"
>
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
+ <include layout="@layout/notification_close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="end"
+ android:layout_marginEnd="20dp"
/>
- </FrameLayout>
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index ce8a904..13f2c37 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -107,13 +107,20 @@
>
<!--expand_button_container is dynamically placed between here and at the end of the
layout. It starts here since only FrameLayout layout params have gravity-->
- <FrameLayout
+ <LinearLayout
android:id="@+id/expand_button_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end|top"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:orientation="vertical">
+ <include layout="@layout/notification_close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="end"
+ android:layout_marginEnd="20dp"
+ />
<!--expand_button_touch_container makes sure that we can nicely center the expand
content in the collapsed layout while the parent makes sure that we're never laid out
bigger than the messaging content.-->
@@ -145,6 +152,6 @@
android:layout_gravity="center"
/>
</LinearLayout>
- </FrameLayout>
+ </LinearLayout>
</FrameLayout>
</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 0c6d49e..8a98296 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -484,7 +484,7 @@
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Laat die program toe om jou Android TV-toestel se oproeprekord te wysig, insluitend data oor inkomende en uitgaande oproepe. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te wysig."</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Laat die program toe om jou foon se oproeprekord, insluitende data oor inkomende en uitgaande oproepe, te verander. Kwaadwillige programme kan dit gebruik om jou oproeprekord uit te vee of te verander."</string>
<string name="permlab_bodySensors" msgid="662918578601619569">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl program gebruik word"</string>
- <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Gee die program toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl die program gebruik word."</string>
+ <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"Gee die app toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl die app gebruik word."</string>
<string name="permlab_bodySensors_background" msgid="4912560779957760446">"Kry toegang tot liggaamsensordata, soos polsslag, terwyl program op agtergrond is"</string>
<string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"Gee die program toegang tot liggaamsensordata, soos polsslag, temperatuur en bloedsuurstofpersentasie, terwyl dit op die agtergrond is."</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"Lees kalendergebeurtenisse en -besonderhede"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Kies om draadlose ontfouting te deaktiveer."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Toetsraamwerkmodus is geaktiveer"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Voer \'n fabriekterugstelling uit om Toetsraamwerkmodus te deaktiveer."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Reekskonsole is geaktiveer"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Werkverrigting word beïnvloed. Gaan selflaaiprogram na om te deaktiveer."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentele MTE is geaktiveer"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Los die volumesleutels. Druk en hou albei volumesleutels weer 3 sekondes lank in om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aan te skakel."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Kies \'n kenmerk om te gebruik wanneer jy op die toeganklikheidknoppie tik:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Kies \'n kenmerk om te gebruik saam met die toeganklikheidgebaar (swiep met twee vingers op van die onderkant van die skerm af):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Kies \'n kenmerk om te gebruik saam met die toeganklikheidgebaar (swiep met drie vingers op van die onderkant van die skerm af):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Raak en hou die toeganklikheidknoppie om tussen kenmerke te wissel."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Swiep op met twee vingers en hou om tussen kenmerke te wissel."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Swiep op met drie vingers en hou om tussen kenmerke te wissel."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Vergroting"</string>
<string name="user_switched" msgid="7249833311585228097">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Skakel tans oor na <xliff:g id="NAME">%1$s</xliff:g> …"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Oproep aan die gang"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Keur tans \'n inkomende oproep"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Ongekategoriseer"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Jy stel die belangrikheid van hierdie kennisgewings."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Dit is belangrik as gevolg van die mense wat betrokke is."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Gepasmaakte appkennisgewing"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Toeganklikheidkortpadkieser op skerm"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Toeganklikheidkortpad"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Maak kennisgewingskerm toe"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"D-paneel op"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"D-paneel af"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-paneel links"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 39152fe..864a5d7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ገመድ-አልባ debuggingን ለማሰናከል ይምረጡ።"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"የሙከራ ጥቅል ሁነታ ነቅቷል"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"የመሞከሪያ ጥቅል ሁነታን ለማሰናከል የፋብሪካ ዳግም ቅንብርን ይሞክሩ።"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"ተከታታይ ኮንሶል ነቅቷል"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"አፈጻጸም ተጽዕኖ አርፎበታል። ለማሰናከል፣ bootloader ን ይፈትሹ።"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"የሙከራ MTE ነቅቷል።"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> በርቷል።"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ጠፍተዋል።"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"የድምጽ መጠን ቁልፎቹን ይልቀቁ። <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን ለማብራት ሁለቱንም የድምጽ መጠን ቁልፎች በድጋሚ ለ3 ሰከንዶች ተጭነው ይያዙ።"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"የተደራሽነት አዝራርን መታ በሚያደርጉበት ጊዜ ጥቅም ላይ የሚውለውን ባሕሪ ይምረጡ፦"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ከተደራሽነት ጣት ምልክት ጋር የሚጠቀሙበት ባሕሪ ይምረጡ (በሁለት ጣቶች ከማያ ገጹ ግርጌ ወደ ላይ ይጥረጉ)፦"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ከተደራሽነት ጣት ምልክት ጋር አብረው የሚጠቀሙበት ባሕሪ ይምረጡ (በሦስት ጣቶች ከማያ ገጹ ግርጌ ወደ ላይ ይጥረጉ)፦"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"በባሕሪያት መካከል ለመቀያየር የተደራሽነት አዝራሩን ነክተው ይያዙ።"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"በባሕሪያት መካከል ለመቀያየር በሁለት ጣቶች ወደ ላይ ጠርገው ይያዙ።"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"በባሕሪያት መካከል ለመቀያየር በሶስት ጣቶች ወደ ላይ ጠርገው ይያዙ።"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ማጉላት"</string>
<string name="user_switched" msgid="7249833311585228097">"የአሁኑ ተጠቃሚ <xliff:g id="NAME">%1$s</xliff:g>።"</string>
<string name="user_switching_message" msgid="1912993630661332336">"ወደ <xliff:g id="NAME">%1$s</xliff:g> በመቀየር ላይ…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"እየተካሄደ ያለ ጥሪ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ገቢ ጥሪ ማጣራት"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"ያልተመደቡ"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"የእነዚህን ማሳወቂያዎች አስፈላጊነት አዘጋጅተዋል።"</string>
<string name="importance_from_person" msgid="4235804979664465383">"ይሄ በሚሳተፉ ሰዎች ምክንያት አስፈላጊ ነው።"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ብጁ የመተግበሪያ ማሳወቂያ"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"የማያ ገፅ ላይ ተደራሽነት አቋራጭ መራጭ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"የተደራሽነት አቋራጭ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"የማሳወቂያ ጥላን አሰናብት"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"ከDpad በላይ"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"ከDpad በታች"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ከDpad በስተግራ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 3d066d9..eb2eea5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1415,6 +1415,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"انقر لإيقاف ميزة \"تصحيح الأخطاء اللاسلكي\""</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"تم تفعيل وضع \"مفعّل الاختبار\""</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"يمكنك إجراء إعادة ضبط على الإعدادات الأصلية لإيقاف وضع \"مفعِّل اختبار\"."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"وحدة التحكّم التسلسلية مفعّلة"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"الأداء متأثر. لإيقاف وحدة التحكّم، تحقّق من برنامج الإقلاع."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"الميزة التجريبية إضافة وضع علامات الذاكرة (MTE) مفعّلة"</string>
@@ -1759,12 +1763,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ارفع إصبعَيك عن مفتاحَي مستوى الصوت. لتفعيل خدمة \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"، اضغط مع الاستمرار على كلا مفتاحَي مستوى الصوت مجددًا لمدة 3 ثوانٍ."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"يمكنك اختيار إحدى الميزات لاستخدامها عند النقر على زر أدوات تمكين الوصول:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"يمكنك اختيار إحدى الميزات لاستخدامها مع إيماءة أدوات تمكين الوصول (مرّر سريعًا إلى الأعلى من أسفل الشاشة باستخدام إصبعين):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"يمكنك اختيار إحدى الميزات التالية لاستخدامها مع إيماءة أدوات تمكين الوصول (مرّر سريعًا إلى الأعلى من أسفل الشاشة باستخدام ثلاثة أصابع):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"للتبديل بين الخدمات، يمكنك النقر مع الاستمرار على زر أدوات تمكين الوصول."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"للتبديل بين الخدمات، مرّر سريعًا من أسفل الشاشة إلى أعلاها باستخدام إصبعين مع تثبيتهما."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"للتبديل بين الميزات، مرّر سريعًا من أسفل الشاشة لأعلاها بثلاثة أصابع مع تثبيتها."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"التكبير"</string>
<string name="user_switched" msgid="7249833311585228097">"المستخدم الحالي <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"جارٍ التبديل إلى <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1980,6 +1990,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"مكالمة جارية"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"يتم فحص المكالمة الواردة"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"غير مصنفة"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"العروض الترويجية"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"التواصل الاجتماعي"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"الأخبار"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"الاقتراحات"</string>
<string name="importance_from_user" msgid="2782756722448800447">"لقد عيَّنت أهمية هذه الإشعارات."</string>
<string name="importance_from_person" msgid="4235804979664465383">"هذه الرسالة مهمة نظرًا لأهمية الأشخاص المعنيين."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"إشعار تطبيق مخصّص"</string>
@@ -2196,6 +2210,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"أداة اختيار اختصارات أدوات تمكين الوصول على الشاشة"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"اختصارات أدوات تمكين الوصول"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"إغلاق مركز الإشعارات"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"القائمة"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"تشغيل الوسائط أو إيقافها مؤقتًا"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"زرّ الاتجاه للأعلى"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"زرّ الاتجاه للأسفل"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"زرّ الاتجاه لليسار"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index b9c072b..28d5f70 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -606,12 +606,12 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"এপ্টোক নিকটৱৰ্তী ব্লুটুথ ডিভাইচ বিচাৰি উলিয়াবলৈ আৰু সেইসমূহৰ সৈতে পেয়াৰ কৰিবলৈ অনুমতি দিয়ে"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"পেয়াৰ কৰা ব্লুটুথ ডিভাইচৰ সৈতে সংযোগ কৰক"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"এপ্টোক পেয়াৰ কৰা ব্লুটুথ ডিভাইচৰ সৈতে সংযোগ কৰিবলৈ অনুমতি দিয়ে"</string>
- <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"নিকটৱৰ্তী ব্লুটুথ ডিভাইচত বিজ্ঞাপন প্ৰচাৰ কৰা"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"এপ্টোক নিকটৱৰ্তী ব্লুটুথ ডিভাইচত বিজ্ঞাপন প্ৰচাৰ কৰিবলৈ দিয়ে"</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"নিকটৱৰ্তী ব্লুটুথ ডিভাইচত বিজ্ঞাপন প্ৰচাৰাভিযান কৰা"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"এপ্টোক নিকটৱৰ্তী ব্লুটুথ ডিভাইচত বিজ্ঞাপন প্ৰচাৰাভিযান কৰিবলৈ দিয়ে"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰক"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"এপ্টোক নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচসমূহৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ক"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"নিকটৱৰ্তী ৱাই-ফাই ডিভাইচসমূহৰ সৈতে ভাব বিনিময় কৰক"</string>
- <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"এপ্টোক বিজ্ঞাপন প্ৰচাৰ কৰিবলৈ, সংযোগ কৰিবলৈ আৰু নিকটৱৰ্তী ৱাই-ফাই ডিভাইচৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে"</string>
+ <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"এপ্টোক বিজ্ঞাপন প্ৰচাৰাভিযান কৰিবলৈ, সংযোগ কৰিবলৈ আৰু নিকটৱৰ্তী ৱাই-ফাই ডিভাইচৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"অগ্ৰাধিকাৰ দিয়া NFC পৰিশোধ সেৱাৰ তথ্য"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"এপ্টোক অগ্ৰাধিকাৰ দিয়া nfc পৰিশোধ সেৱাৰ পঞ্জীকৃত সহায়কসমূহ আৰু পৰিশোধ কৰিব লগা লক্ষ্যস্থান দৰে তথ্য পাবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ৱায়াৰলেচ ডি\'বাগিং অক্ষম কৰিবলৈ বাছনি কৰক।"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"টেষ্ট হাৰনেছ ম’ড সক্ষম কৰা আছে"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"টেষ্ট হাৰনেছ ম’ড অক্ষম কৰিবলৈ ফেক্টৰী ৰিছেট কৰক।"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"ক্ৰমিক কনছ’ল সক্ষম কৰা আছে"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"কাৰ্যক্ষমতা প্ৰভাৱিত হৈছে। অক্ষম কৰিবলৈ বুটল’ডাৰ পৰীক্ষা কৰক।"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"পৰীক্ষামূলক MTE সক্ষম কৰা হ\'ল"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কীসমূহ ধৰি ৰাখক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰা হ\'ল।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধৰি ৰাখিছিল। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অফ কৰা হ\'ল।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ভলিউম কী এৰি দিয়ক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰিবলৈ, দুয়োটা ভলিউম কী পুনৰ ৩ ছেকেণ্ডৰ বাবে টিপি হেঁচি ৰাখক।"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"আপুনি সাধ্য-সুবিধাৰ বুটামটো টিপিলে ব্যৱহাৰ কৰিবলৈ এটা সুবিধা বাছনি কৰক:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"সাধ্য-সুবিধাৰ নির্দেশৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ এটা সুবিধা বাছনি কৰক (দুটা আঙুলিৰে স্ক্রীনখনৰ একেবাৰে তলিৰ পৰা ওপৰলৈ ছোৱাইপ কৰক):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"সাধ্য-সুবিধাৰ নির্দেশৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ এটা সুবিধা বাছনি কৰক (তিনিটা আঙুলিৰে স্ক্রীনখনৰ একেবাৰে তলিৰ পৰা ওপৰলৈ ছোৱাইপ কৰক):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"সুবিধাসমূহ সালসলনিকৈ ব্যৱহাৰ কৰিবলৈ সাধ্য-সুবিধাৰ বুটামটো স্পৰ্শ কৰি ধৰি ৰাখক।"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"সুবিধাসমূহ সালসলনিকৈ ব্যৱহাৰ কৰিবলৈ দুটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক।"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"সুবিধাসমূহ সালসলনিকৈ ব্যৱহাৰ কৰিবলৈ তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক।"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"বিবৰ্ধন"</string>
<string name="user_switched" msgid="7249833311585228097">"বৰ্তমানৰ ব্যৱহাৰকাৰী <xliff:g id="NAME">%1$s</xliff:g>।"</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>লৈ সলনি কৰি থকা হৈছে…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"চলি থকা কল"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"এটা অন্তৰ্গামী কলৰ পৰীক্ষা কৰি থকা হৈছে"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"শ্ৰেণীবদ্ধ নকৰা"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"প্ৰমোশ্বন"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"সামাজিক"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"বাতৰি"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"চুপাৰিছ"</string>
<string name="importance_from_user" msgid="2782756722448800447">"এই জাননীবোৰৰ গুৰুত্ব আপুনি ছেট কৰব লাগিব।"</string>
<string name="importance_from_person" msgid="4235804979664465383">"এই কার্যৰ সৈতে জড়িত থকা লোকসকলক ভিত্তি কৰি এইয়া গুৰুত্বপূর্ণ বুলি বিবেচনা কৰা হৈছ।"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"কাষ্টম এপৰ জাননী"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"স্ক্ৰীনত সাধ্য সুবিধাসমূহৰ শ্বৰ্টকাট বাছনি কৰাৰ সুবিধা"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"সাধ্য সুবিধাৰ শ্বৰ্টকাট"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"জাননী পেনেল অগ্ৰাহ্য কৰক"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"মেনু"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"মিডিয়া প্লে’/পজ কৰক"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"ডিপেডৰ ওপৰফালৰ বুটাম"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"ডিপেডৰ তলফালৰ বুটাম"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ডিপেডৰ বাওঁফালৰ বুটাম"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 32210f7..8d68777 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"WiFi sazlamasını deaktiv etmək üçün seçin."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test Rejimi aktivdir"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Test Rejimini deaktiv etmək üçün fabrika ayarlarına sıfırlayın."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Ardıcıl konsol aktiv edildi"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performans təsirlənir. Söndürməkçün yükləyicini yoxlayın."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimental MTE aktiv edilib"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Səs düymələrini buraxın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini aktiv etmək üçün hər iki səs düyməsinə yenidən 3 saniyə basıb saxlayın."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Xüsusi imkanlar düyməsinə toxunanda istədiyiniz funksiyanı seçin:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Əlçatımlılıq jesti (iki barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Əlçatımlılıq jesti (üç barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalar arasında keçid etmək üçün xüsusi imkanlar düyməsini basıb saxlayın."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funksiyalar arasında keçid etmək üçün iki barmağınızla yuxarı sürüşdürüb saxlayın."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funksiyalar arasında keçid etmək üçün üç barmağınızla yuxarı doğru sürüşdürüb saxlayın."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Böyütmə"</string>
<string name="user_switched" msgid="7249833311585228097">"Cari istifadəçi <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> adına keçirilir…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Davam edən zəng"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Gələn zəng göstərilir"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Kateqoriyasız"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Tanıtımlar"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Sosial"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Xəbərlər"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Tövsiyələr"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Bildirişlərin əhəmiyyətini Siz ayarlaryırsınız."</string>
<string name="importance_from_person" msgid="4235804979664465383">"İnsanlar cəlb olunduğu üçün bu vacibdir."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Fərdi tətbiq bildirişi"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekranda Əlçatımlılıq Qısayolu Seçicisi"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Əlçatımlılıq Qısayolu"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Bildiriş Göstərişini qapadın"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menyu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Medianı oxudun/durdurun"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad Yuxarı"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad Aşağı"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Sola"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ac01883..c537fff 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Izaberite da biste onemogućili bežično otklanjanje grešaka."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Omogućen je režim probnog korišćenja"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Obavite resetovanje na fabrička podešavanja da biste onemogućili režim probnog korišćenja."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola je omogućena"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performanse su smanjene. Da biste onemogući konzolu, proverite pokretački program."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentalni MTE je omogućen"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Pustite tastere za jačinu zvuka. Da biste uključili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ponovo pritisnite i zadržite oba tastera za jačinu zvuka 3 sekunde."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Izaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Odaberite funkciju koja će se koristiti pomoću pokreta za pristupačnost (pomoću dva prsta prevucite nagore od dna ekrana):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Odaberite funkciju koja će se koristiti pomoću pokreta za pristupačnost (pomoću tri prsta prevucite nagore od dna ekrana):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Da biste prelazili sa jedne funkcije na drugu, dodirnite i zadržite dugme Pristupačnost."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Da biste prelazili sa jedne funkcije na drugu, prevucite nagore pomoću dva prsta i zadržite."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Da biste prelazili sa jedne funkcije na drugu, prevucite nagore pomoću tri prsta i zadržite."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Uvećanje"</string>
<string name="user_switched" msgid="7249833311585228097">"Aktuelni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Prebacivanje na <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv je u toku"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Proverava se dolazni poziv"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Nekategorizovano"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promocije"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Društvene mreže"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Vesti"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Preporuke"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Vi podešavate važnost ovih obaveštenja."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ovo je važno zbog ljudi koji učestvuju."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Prilagođeno obaveštenje o aplikaciji"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Alatka za biranje prečica za pristupačnost na ekranu"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Prečica za pristupačnost"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Odbaci traku sa obaveštenjima"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Meni"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Pusti/pauziraj medije"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"nagore na D-pad-u"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"nadole na D-pad-u"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"nalevo na D-pad-u"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index cbcc90a..e6391df 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Выберыце, каб выключыць адладку па Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Тэставы рэжым уключаны"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Каб выключыць тэставы рэжым, скіньце налады да заводскіх значэнняў."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Паслядоўная кансоль уключана"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Паказчык прадукцыйнасці змяніўся. Каб выключыць кансоль, праверце загрузчык."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Эксперыментальнае пашырэнне тэгаў памяці (MTE) уключана"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Адпусціце клавішы гучнасці. Каб уключыць сэрвіс \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце абедзве клавішы гучнасці яшчэ раз і ўтрымлівайце іх на працягу 3 секунд."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Выберыце функцыю, якую будзеце выкарыстоўваць пры націску кнопкі спецыяльных магчымасцей:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Выберыце функцыю, якую будзеце выкарыстоўваць з жэстам спецыяльных магчымасцей (правесці двума пальцамі па экране знізу ўверх):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Выберыце функцыю, якую будзеце выкарыстоўваць з жэстам спецыяльных магчымасцей (правесці трыма пальцамі па экране знізу ўверх):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Каб пераключыцца на іншыя функцыі, націсніце і ўтрымлівайце кнопку спецыяльных магчымасцей."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Каб пераключыцца на іншую функцыю, правядзіце ўверх двума пальцамі і ўтрымлівайце іх на экране."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Каб пераключыцца на іншую функцыю, правядзіце ўверх трыма пальцамі і ўтрымлівайце іх на экране."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Павелічэнне"</string>
<string name="user_switched" msgid="7249833311585228097">"Бягучы карыстальнік <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Пераход у рэжым \"<xliff:g id="NAME">%1$s</xliff:g>\"..."</string>
@@ -1978,6 +1988,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Бягучы выклік"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Фільтраванне ўваходнага выкліку"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Некатэгарызаванае"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Вы задалі важнасць гэтых апавяшчэнняў."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Гэта важна, бо з гэтым звязаны пэўныя людзі."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Апавяшчэнне пра карыстальніцкую праграму"</string>
@@ -2194,6 +2212,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Налада хуткага доступу да спецыяльных магчымасцей на экране"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Хуткі доступ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Закрыць шчыток апавяшчэнняў"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Уверх на панэлі кіравання"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Уніз на панэлі кіравання"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Улева на панэлі кіравання"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index affa311..4a12c2b 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Изберете, за да деактивирате безжичното отстраняване на грешки."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Режимът за тестова среда е активиран"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Възстановете фабричните настройки, за да деактивирате режима за тестова среда."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Серийната конзола е активирана"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Ефективността е засегната. За да деактивирате, проверете програмата за първоначално зареждане."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Експерименталното разширение MTE е активирано"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е включена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е изключена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Освободете бутоните за силата на звука. За да включите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, отново натиснете двата бутона за силата на звука и задръжте за 3 секунди."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Изберете функция, която да използвате, когато докоснете бутона за достъпност:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Изберете коя функция да се използва с жеста за достъпност (прекарване на два пръста нагоре от долната част на екрана):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Изберете коя функция да се използва с жеста за достъпност (прекарване на три пръста нагоре от долната част на екрана):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"За превключване между функциите докоснете и задръжте бутона за достъпност."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"За превключване между функциите прекарайте два пръста нагоре и задръжте."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"За превключване между функциите прекарайте три пръста нагоре и задръжте."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Увеличение"</string>
<string name="user_switched" msgid="7249833311585228097">"Текущ потребител <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Превключва се към: <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Текущо обаждане"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Преглежда се входящо обаждане"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Некатегоризирани"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Промоции"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Социални мрежи"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Новини"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Препоръки"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Зададохте важността на тези известия."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Това е важно заради участващите хора."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Персонализирано известие за приложение"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Инструмент за избор на пряк път към достъпността на екрана"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Пряк път за достъпност"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Отхвърляне на падащия панел с известия"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Меню"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Пускане/пауза на мултимедията"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Контролен пад – горе"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Контролен пад – долу"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Контролен пад – ляво"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 889de67..b895d7f 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ওয়্যারলেস ডিবাগিং বন্ধ করতে বেছে নিন।"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"টেস্ট হারনেস মোড চালু আছে"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"টেস্ট হারনেস মোড বন্ধ করতে ফ্যাক্টরি রিসেট করুন।"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"সিরিয়াল কনসোল চালু করা হয়েছে"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"পারফর্ম্যান্সে এর প্রভাব পড়বে। চালানো বন্ধ করতে \'বুটলোডার\' প্রোগ্রামে এটিকে চেক করে দেখুন।"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"পরীক্ষামূলক MTE চালু আছে"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ভলিউম \'কী\' রিলিজ করুন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করুন, দু\'টি ভলিউম \'কী\' আবার প্রেস করে ৩ সেকেন্ড ধরে রাখুন।"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"অ্যাক্সেসিবিলিটি বোতামে ট্যাপ করে আপনি যে ফিচার ব্যবহার করতে চান সেটি বেছে নিন:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"অ্যাক্সেসিবিলিটি জেসচারের (দুটি আঙ্গুল দিয়ে স্ক্রিনের নিচে থেকে উপরের দিকে সোয়াইপ করুন) সাহায্যে আপনি যে ফিচার ব্যবহার করতে চান সেটি বেছে নিন:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"অ্যাক্সেসিবিলিটি জেসচারের (তিনটি আঙ্গুল দিয়ে স্ক্রিনের নিচে থেকে উপরের দিকে সোয়াইপ করুন) সাহায্যে আপনি যে ফিচার ব্যবহার করতে চান সেটি বেছে নিন:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"একটি ফিচার থেকে অন্যটিতে যেতে অ্যাক্সেসিবিলিটি বোতাম টাচ করে ধরে থাকুন।"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"একটি ফিচার থেকে অন্যটিতে যেতে, দুটি আঙ্গুল দিয়ে নিচে থেকে উপরের দিকে সোয়াইপ করে ধরে থাকুন।"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"একটি ফিচার থেকে অন্যটিতে যেতে, তিনটি আঙ্গুল দিয়ে উপরের দিকে সোয়াইপ করে ধরে থাকুন।"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"বড় করে দেখা"</string>
<string name="user_switched" msgid="7249833311585228097">"বর্তমান ব্যবহারকারী <xliff:g id="NAME">%1$s</xliff:g>৷"</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> প্রোফাইলে পাল্টানো হচ্ছে…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"চালু থাকা কল"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ইনকামিং কল স্ক্রিনিং করা হচ্ছে"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"বিভাগ নির্ধারিত নয়"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"প্রোমোশন"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"সোশ্যাল"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"খবর"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"সাজেশন"</string>
<string name="importance_from_user" msgid="2782756722448800447">"আপনি এই বিজ্ঞপ্তিগুলির গুরুত্ব সেট করেছেন।"</string>
<string name="importance_from_person" msgid="4235804979664465383">"লোকজন জড়িত থাকার কারণে এটি গুরুত্বপূর্ণ।"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"কাস্টম অ্যাপ বিজ্ঞপ্তি"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"অন-স্ক্রিন অ্যাক্সেসিবিলিটি শর্টকাট বেছে নেওয়ার বিকল্প"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"অ্যাক্সেসিবিলিটি শর্টকাট"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"বিজ্ঞপ্তি শেড বাতিল করুন"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"মেনু"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"মিডিয়া প্লে/পজ করুন"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"ডিপ্যাড (Dpad)-এর উপরে"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"ডিপ্যাড (Dpad)-এর নিচে"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ডিপ্যাড (Dpad)-এর বাঁদিকে"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index af9ac56..0e4ce17 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Odaberite da onemogućite bežično otklanjanje grešaka."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Omogućen način rada okvira za testiranje"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Izvršite vraćanje na fabričke postavke da onemogućite način rada okvira za testiranje."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola omogućena"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performanse su smanjene. Da onemogućite, provjerite program za učitavanje operativnog sistema."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentalni MTE je omogućen"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Pustite tipke za jačinu zvuka. Da uključite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ponovo pritisnite i zadržite obje tipke za jačinu zvuka 3 sekunde."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Odaberite funkciju koju ćete koristiti kada izvedete pokret za pristupačnost (s dva prsta prevucite prema gore s dna ekrana):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Odaberite funkciju koju ćete koristiti kada izvedete pokret za pristupačnost (s tri prsta prevucite prema gore s dna ekrana):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Za prebacivanje između funkcija, dodirnite i zadržite dugme Pristupačnost."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Da prebacujete između funkcija, s dva prsta prevucite prema gore i zadržite."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Da prebacujete između funkcija, s tri prsta prevucite prema gore i zadržite."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Uvećavanje"</string>
<string name="user_switched" msgid="7249833311585228097">"Trenutni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Prebacivanje na korisnika <xliff:g id="NAME">%1$s</xliff:g>..."</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv u toku"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtriranje dolaznog poziva"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Nije kategorizirano"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promocije"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Društvene mreže"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Vijesti"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Preporuke"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Vi određujete značaj ovih obavještenja."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ovo je značajno zbog osoba koje su uključene."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Prilagođeno obavještenje aplikacije"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Okvir za odabir prečice za pristupačnost na ekranu"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Prečica za pristupačnost"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Odbacite lokaciju za obavještenja"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Izbornik"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Reproduciraj/pauziraj medijske sadržaje"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Upravljač gore"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Upravljač dolje"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Upravljač lijevo"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 66c81e6..c1b740a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -157,7 +157,7 @@
<string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Seguretat de xarxes mòbils"</string>
<string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Encriptació, notificacions de xarxes sense encriptació"</string>
<string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"S\'ha accedit a l\'identificador de dispositiu"</string>
- <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"A les <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una xarxa propera ha registrat l\'identificador únic del teu dispositiu (IMSI or IMEI) mentre s\'utilitzava la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
+ <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"A les <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una xarxa propera ha registrat l\'ID únic del dispositiu (IMSI or IMEI) amb la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
<string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"A les <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, una xarxa propera ha registrat l\'identificador únic del teu dispositiu (IMSI or IMEI) mentre s\'utilitzava la SIM de <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nAixò vol dir que s\'han registrat la teva ubicació, activitat o identitat. Tot i ser una pràctica habitual, pot suposar un problema per a aquelles persones preocupades per la privadesa."</string>
<string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"T\'has connectat a la xarxa encriptada <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
<string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"La connexió SIM de <xliff:g id="NETWORK_NAME">%1$s</xliff:g> ara és més segura"</string>
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecciona per desactivar la depuració sense fil."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"S\'ha activat el mode Agent de prova"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Si vols desactivar el mode Agent de prova, restableix les dades de fàbrica."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"S\'ha activat la consola de sèrie"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Afecta el rendiment. Per desactivar-la, comprova el bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"L\'MTE experimental s\'ha activat"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Deixa anar les tecles de volum. Per activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, torna a mantenir premudes totes dues tecles de volum durant 3 segons."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Tria la funció que vols utilitzar quan toquis el botó d\'accessibilitat:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Tria la funció que vols utilitzar amb el gest d\'accessibilitat (llisca cap amunt amb dos dits des de la part inferior de la pantalla):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Tria la funció que vols utilitzar amb el gest d\'accessibilitat (fes lliscar tres dits cap amunt des de la part inferior de la pantalla):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Per canviar entre funcions, mantén premut el botó d\'accessibilitat."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Per canviar entre funcions, llisca cap amunt amb dos dits i mantén premut."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Per canviar entre funcions, llisca cap amunt amb tres dits i mantén premut."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ampliació"</string>
<string name="user_switched" msgid="7249833311585228097">"Usuari actual: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"S\'està canviant a <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Trucada en curs"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"S\'està filtrant una trucada entrant"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sense classificar"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Has definit la importància d\'aquestes notificacions."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Aquest missatge és important per les persones implicades."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificació d\'aplicació personalitzada"</string>
@@ -2193,6 +2211,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selector de dreceres d\'accessibilitat en pantalla"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Drecera d\'accessibilitat"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ignora l\'àrea de notificacions"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Creu direccional: amunt"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Creu direccional: avall"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Creu direccional: esquerra"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ad43a95..3caf4a2 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Vyberte, chcete-li zakázat bezdrátové ladění."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Režim správce testů je aktivní"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Chcete-li deaktivovat režim správce testů, restartujte zařízení do továrního nastavení."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Je zapnutá sériová konzole"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Toto má dopad na výkon. Chcete-li ji vypnout, zkontrolujte bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Je zapnuto experimentální MTE"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Uvolněte tlačítka hlasitosti. Pokud chcete zapnout službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, znovu tři sekundy podržte obě tlačítka hlasitosti."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Určete, jakou funkci aktivujete klepnutím na tlačítko přístupnosti:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Určete, jakou funkci aktivujete pomocí gesta přístupnosti (přejetí dvěma prsty ze spodní části obrazovky nahoru):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Určete, jakou funkci aktivujete pomocí gesta přístupnosti (přejetí třemi prsty ze spodní části obrazovky nahoru):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Chcete-li přepnout mezi funkcemi, podržte tlačítko přístupnosti."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Chcete-li přepnout mezi funkcemi, přejeďte nahoru dvěma prsty a podržte je."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Chcete-li přepnout mezi funkcemi, přejeďte nahoru třemi prsty a podržte je."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Zvětšení"</string>
<string name="user_switched" msgid="7249833311585228097">"Aktuální uživatel je <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Přepínání na uživatele <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1978,6 +1988,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Probíhající hovor"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Prověřování příchozího hovoru"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Neklasifikováno"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Důležitost oznámení určujete vy."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Tato zpráva je důležitá kvůli lidem zapojeným do konverzace."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Vlastní oznámení aplikace"</string>
@@ -2194,6 +2212,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Výběr zkratky přístupnosti na obrazovce"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Zkratka přístupnosti"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Zavřít panel oznámení"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad nahoru"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad dolů"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad doleva"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index e32b0b6..66daf44 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Vælg for at deaktivere trådløs fejlretning."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Tilstanden Testsele er aktiveret"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Gendan fabriksindstillingerne for at deaktivere tilstanden Testsele."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Seriekonsollen er aktiveret"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Effektiviteten er påvirket. Deaktiver via bootloaderen."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentel MTE er aktiveret"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Slip lydstyrkeknapperne. Du kan aktivere <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ved at holde begge lydstyrkeknapper nede igen i 3 sekunder."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Vælg, hvilken funktion du vil bruge, når du trykker på knappen til hjælpefunktioner:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Vælg, hvilken funktion du vil bruge, når du laver bevægelsen for hjælpefunktioner (stryger opad fra bunden af skærmen med to fingre):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Vælg, hvilken funktion du vil bruge, når du laver bevægelsen for hjælpefunktioner (stryger opad fra bunden af skærmen med tre fingre):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Du kan skifte mellem funktioner ved at holde knappen til hjælpefunktioner nede."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Du kan skifte mellem funktioner ved at stryge opad med to fingre og holde dem nede."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Du kan skifte mellem funktioner ved at stryge opad med tre fingre og holde dem nede."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Forstørrelse"</string>
<string name="user_switched" msgid="7249833311585228097">"Nuværende bruger <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Skifter til <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Igangværende opkald"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Et indgående opkald screenes"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Uden kategori"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Du angiver, hvor vigtige disse notifikationer er."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Dette er vigtigt på grund af de personer, det handler om."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Tilpasset appnotifikation"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Valg af genvej til hjælpefunktioner på skærmen"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Genvej til hjælpefunktioner"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Luk notifikationspanel"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"D-pad, op"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"D-pad, ned"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad, venstre"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ca858790..492bbeb 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Auswählen, um \"Debugging über WLAN\" zu deaktivieren."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test-Harnischmodus aktiviert"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Setz das Gerät auf die Werkseinstellungen zurück, um den Test-Harnischmodus zu deaktivieren."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serielle Konsole aktiviert"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Leistung wird beeinflusst. Überprüfe Bootloader zum Deaktivieren."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimentelle MTE aktiviert"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lass die Lautstärketasten los. Halte zum Aktivieren von <xliff:g id="SERVICE_NAME">%1$s</xliff:g> beide Lautstärketasten noch einmal 3 Sekunden lang gedrückt."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Wähle eine Funktion aus, die verwendet wird, wenn du auf die Schaltfläche \"Bedienungshilfen\" tippst:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Wähle die Funktion aus, die mit der Touch-Geste für die Bedienungshilfen verwendet werden soll (mit zwei Fingern vom unteren Bildschirmrand nach oben wischen):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Wähle eine Funktion aus, die mit der Touch-Geste für die Bedienungshilfen verwendet werden soll (mit drei Fingern vom unteren Bildschirmrand nach oben wischen):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Wenn du zwischen den Funktionen wechseln möchtest, halte die Schaltfläche „Bedienungshilfen“ gedrückt."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Wenn du zwischen den Funktionen wechseln möchtest, wische mit zwei Fingern nach oben und halte."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Wenn du zwischen den Funktionen wechseln möchtest, wische mit drei Fingern nach oben und halte."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Vergrößerung"</string>
<string name="user_switched" msgid="7249833311585228097">"Aktueller Nutzer <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="user_switching_message" msgid="1912993630661332336">"Wechseln zu <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Aktueller Anruf"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filter für eingehenden Anruf"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Unkategorisiert"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Du hast die Wichtigkeit dieser Benachrichtigungen festgelegt."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Diese Benachrichtigung ist aufgrund der beteiligten Personen wichtig."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Benutzerdefinierte App-Benachrichtigung"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Auswahl für Kurzbefehle für Bildschirmbedienungshilfen"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Kurzbefehl für Bedienungshilfen"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Benachrichtigungsleiste schließen"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Steuerkreuz nach oben"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Steuerkreuz nach unten"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Steuerkreuz nach links"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index cb1d3b1..7b1c7c8 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Επιλέξτε, για να απενεργοποιήσετε τον ασύρματο εντοπισμό σφαλμάτων."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Η λειτουργία περιβάλλοντος δοκιμών ενεργοποιήθηκε"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Εκτελέστε επαναφορά εργοστασιακών ρυθμίσεων για να απενεργοποιήσετε τη λειτουργία περιβάλλοντος δοκιμών."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Η σειριακή κονσόλα ενεργοποιήθηκε"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Η απόδοση επηρεάζεται. Για απενεργοποίηση, επιλέξτε το πρόγραμμα φόρτωσης εκκίνησης."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Ενεργοποιήθηκε το πειραματικό MTE"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ενεργοποιήθηκε."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>: απενεργοποιημένο"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Αφήστε τα κουμπιά έντασης ήχου. Για να ενεργοποιήσετε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, πατήστε ξανά παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Επιλέξτε μια λειτουργία που θα χρησιμοποιείται κατά το πάτημα του κουμπιού προσβασιμότητας:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Επιλέξτε μια λειτουργία που θα χρησιμοποιείται με την κίνηση προσβασιμότητας (σύρετε με δύο δάχτυλα προς τα επάνω από το κάτω μέρος της οθόνης):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Επιλέξτε μια λειτουργία που θα χρησιμοποιείται με την κίνηση προσβασιμότητας (σύρετε με τρία δάχτυλα προς τα επάνω από το κάτω μέρος της οθόνης):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Για εναλλαγή μεταξύ λειτουργιών, αγγίξτε παρατεταμένα το κουμπί προσβασιμότητας."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Για εναλλαγή μεταξύ λειτουργιών, σύρετε παρατεταμένα με δύο δάχτυλα προς τα επάνω."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Για εναλλαγή μεταξύ λειτουργιών, σύρετε παρατεταμένα με τρία δάχτυλα προς τα επάνω."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Μεγιστοποίηση"</string>
<string name="user_switched" msgid="7249833311585228097">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Εναλλαγή σε <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Κλήση σε εξέλιξη"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Διαλογή εισερχόμενης κλήσης"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Μη κατηγοριοποιημένο"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Μπορείτε να ρυθμίσετε τη βαρύτητα αυτών των ειδοποιήσεων."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Αυτό είναι σημαντικό λόγω των ατόμων που συμμετέχουν."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Προσαρμοσμένη ειδοποίηση εφαρμογής"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Επιλογέας συντόμευσης οθόνης για την προσβασιμότητα"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Συντόμευση προσβασιμότητας"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Παράβλεψη πλαισίου σκίασης ειδοποιήσεων"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad επάνω"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad κάτω"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad αριστερά"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index e748648..159ef51 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Select to disable wireless debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Perform a factory reset to disable Test Harness Mode."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serial console enabled"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performance is impacted. To disable, check bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimental MTE enabled"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for three seconds."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Choose a feature to use when you tap the Accessibility button:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"To switch between features, touch and hold the Accessibility button."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"To switch between features, swipe up with two fingers and hold."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"To switch between features, swipe up with three fingers and hold."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
<string name="user_switched" msgid="7249833311585228097">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Uncategorised"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promotions"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"News"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recommendations"</string>
<string name="importance_from_user" msgid="2782756722448800447">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="4235804979664465383">"This is important because of the people involved."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Custom app notification"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"On-screen accessibility shortcut chooser"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Accessibility shortcut"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Dismiss notification shade"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Media play/pause"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad up"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad down"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 3c1ae77..ed7871f 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1411,6 +1411,8 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Select to disable wireless debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Perform a factory reset to disable Test Harness Mode."</string>
+ <string name="wrong_hsum_configuration_notification_title" msgid="7212758829332714385">"Wrong HSUM build configuration"</string>
+ <string name="wrong_hsum_configuration_notification_message" msgid="5353475441480684381">"The Headless System User Mode state of this device differs from its build configuration. Please factory reset the device."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Serial console enabled"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performance is impacted. To disable, check bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimental MTE enabled"</string>
@@ -1755,12 +1757,12 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for 3 seconds."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Choose a feature to use when you tap the accessibility button:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"To switch between features, touch and hold the accessibility button."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"To switch between features, swipe up with two fingers and hold."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"To switch between features, swipe up with three fingers and hold."</string>
+ <string name="accessibility_button_prompt_text" msgid="6105393217162198616">"Choose a feature"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="6452246951969541792">"Choose a feature"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="77745752309056152">"Choose a feature"</string>
+ <string name="accessibility_button_instructional_text" msgid="8029780800681458835">"The feature will open next time you tap the accessibility button."</string>
+ <string name="accessibility_gesture_instructional_text" msgid="1485998586929977949">"The feature will open next time you use this shortcut. Swipe up with two fingers from the bottom of your screen and release quickly."</string>
+ <string name="accessibility_gesture_3finger_instructional_text" msgid="3430237316928654219">"The feature will open next time you use this shortcut. Swipe up with three fingers from the bottom of your screen and release quickly."</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
<string name="user_switched" msgid="7249833311585228097">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1883,8 +1885,7 @@
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is too short. Must be at least 4 digits."</string>
<string name="restr_pin_try_later" msgid="5897719962541636727">"Try again later"</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
- <!-- no translation found for immersive_cling_description (2896205051090870978) -->
- <skip />
+ <string name="immersive_cling_description" msgid="2896205051090870978">"To exit, swipe down from the top of your screen"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
<string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
@@ -1976,6 +1977,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ongoing call"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Uncategorized"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promotions"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"News"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recommendations"</string>
<string name="importance_from_user" msgid="2782756722448800447">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="4235804979664465383">"This is important because of the people involved."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Custom app notification"</string>
@@ -2192,6 +2197,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"On-screen Accessibility Shortcut Chooser"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Accessibility Shortcut"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Dismiss Notification Shade"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Media Play/Pause"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad Up"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad Down"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 818614a..71a374f 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Select to disable wireless debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Perform a factory reset to disable Test Harness Mode."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serial console enabled"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performance is impacted. To disable, check bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimental MTE enabled"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for three seconds."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Choose a feature to use when you tap the Accessibility button:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"To switch between features, touch and hold the Accessibility button."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"To switch between features, swipe up with two fingers and hold."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"To switch between features, swipe up with three fingers and hold."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
<string name="user_switched" msgid="7249833311585228097">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Uncategorised"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promotions"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"News"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recommendations"</string>
<string name="importance_from_user" msgid="2782756722448800447">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="4235804979664465383">"This is important because of the people involved."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Custom app notification"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"On-screen accessibility shortcut chooser"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Accessibility shortcut"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Dismiss notification shade"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Media play/pause"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad up"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad down"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index a58f731..3e630f3 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Select to disable wireless debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Perform a factory reset to disable Test Harness Mode."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serial console enabled"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performance is impacted. To disable, check bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimental MTE enabled"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for three seconds."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Choose a feature to use when you tap the Accessibility button:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"To switch between features, touch and hold the Accessibility button."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"To switch between features, swipe up with two fingers and hold."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"To switch between features, swipe up with three fingers and hold."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
<string name="user_switched" msgid="7249833311585228097">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Uncategorised"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promotions"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"News"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recommendations"</string>
<string name="importance_from_user" msgid="2782756722448800447">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="4235804979664465383">"This is important because of the people involved."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Custom app notification"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"On-screen accessibility shortcut chooser"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Accessibility shortcut"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Dismiss notification shade"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Media play/pause"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad up"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad down"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 5572714..78fadb3 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1411,6 +1411,8 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Select to disable wireless debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Perform a factory reset to disable Test Harness Mode."</string>
+ <string name="wrong_hsum_configuration_notification_title" msgid="7212758829332714385">"Wrong HSUM build configuration"</string>
+ <string name="wrong_hsum_configuration_notification_message" msgid="5353475441480684381">"The Headless System User Mode state of this device differs from its build configuration. Please factory reset the device."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Serial console enabled"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performance is impacted. To disable, check bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimental MTE enabled"</string>
@@ -1755,12 +1757,12 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for 3 seconds."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Choose a feature to use when you tap the accessibility button:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"To switch between features, touch & hold the accessibility button."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"To switch between features, swipe up with two fingers and hold."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"To switch between features, swipe up with three fingers and hold."</string>
+ <string name="accessibility_button_prompt_text" msgid="6105393217162198616">"Choose a feature"</string>
+ <string name="accessibility_gesture_prompt_text" msgid="6452246951969541792">"Choose a feature"</string>
+ <string name="accessibility_gesture_3finger_prompt_text" msgid="77745752309056152">"Choose a feature"</string>
+ <string name="accessibility_button_instructional_text" msgid="8029780800681458835">"The feature will open next time you tap the accessibility button."</string>
+ <string name="accessibility_gesture_instructional_text" msgid="1485998586929977949">"The feature will open next time you use this shortcut. Swipe up with two fingers from the bottom of your screen and release quickly."</string>
+ <string name="accessibility_gesture_3finger_instructional_text" msgid="3430237316928654219">"The feature will open next time you use this shortcut. Swipe up with three fingers from the bottom of your screen and release quickly."</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Magnification"</string>
<string name="user_switched" msgid="7249833311585228097">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Switching to <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1883,8 +1885,7 @@
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is too short. Must be at least 4 digits."</string>
<string name="restr_pin_try_later" msgid="5897719962541636727">"Try again later"</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
- <!-- no translation found for immersive_cling_description (2896205051090870978) -->
- <skip />
+ <string name="immersive_cling_description" msgid="2896205051090870978">"To exit, swipe down from the top of your screen"</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
<string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
<string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
@@ -1976,6 +1977,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ongoing call"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Uncategorized"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promotions"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"News"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recommendations"</string>
<string name="importance_from_user" msgid="2782756722448800447">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="4235804979664465383">"This is important because of the people involved."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Custom app notification"</string>
@@ -2192,6 +2197,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"On-screen Accessibility Shortcut Chooser"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Accessibility Shortcut"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Dismiss Notification Shade"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Media Play/Pause"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad Up"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad Down"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 94591ea8..86e2b44 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecciona para inhabilitar la depuración inalámbrica."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Se habilitó el modo de agente de prueba"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Restablece la configuración de fábrica para inhabilitar el modo de agente de prueba."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Se habilitó la consola en serie"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Afecta el rendimiento. Para inhabilitarla, verifica el bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimental habilitada"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Suelta las teclas de volumen. Para activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, vuelve a mantener presionadas las teclas de volumen durante 3 segundos."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Elige una función para usar cuando pulses el botón accesibilidad:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Elige la función que se usará cuando realices el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Elige la función que se usará cuando realices el gesto de accesibilidad (deslizar tres dedos hacia arriba desde la parte inferior de la pantalla):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para cambiar entre funciones, mantén presionado el botón de accesibilidad."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para cambiar de función, desliza dos dedos hacia arriba y mantén presionada la pantalla."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para cambiar de función, desliza tres dedos hacia arriba y mantén presionada la pantalla."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ampliación"</string>
<string name="user_switched" msgid="7249833311585228097">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="user_switching_message" msgid="1912993630661332336">"Cambiando a <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Llamada en curso"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando una llamada entrante"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sin categoría"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Estableciste la importancia de estas notificaciones."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Es importante debido a las personas involucradas."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificación de app personalizada"</string>
@@ -2193,6 +2211,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selector del acceso directo de accesibilidad en pantalla"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Acceso directo de accesibilidad"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Descartar panel de notificaciones"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Pad direccional: arriba"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Pad direccional: abajo"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pad direccional: izquierda"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2634494..a12b168 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Toca para desactivar la depuración inalámbrica."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modo de agente de prueba habilitado"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Restablece los ajustes de fábrica para inhabilitar el modo de agente de prueba."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Se ha habilitado la consola en serie"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Afecta al rendimiento. Para inhabilitarlo, comprueba el bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimental habilitado"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Suelta las teclas de volumen. Para activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas las dos teclas de volumen de nuevo durante 3 segundos."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Selecciona la función que se utilizará cuando toques el botón de accesibilidad:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Elige la función que se utilizará con el gesto de accesibilidad (deslizar dos dedos hacia arriba desde la parte inferior de la pantalla):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Elige la función que se utilizará con el gesto de accesibilidad (deslizar tres dedos hacia arriba desde la parte inferior de la pantalla):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para cambiar de una función a otra, mantén pulsado el botón de accesibilidad."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para cambiar de una función a otra, desliza hacia arriba con dos dedos y mantén pulsada la pantalla."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para cambiar de una función a otra, desliza tres dedos hacia arriba y mantén pulsada la pantalla."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ampliación"</string>
<string name="user_switched" msgid="7249833311585228097">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="user_switching_message" msgid="1912993630661332336">"Cambiando a <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Llamada en curso"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando una llamada entrante"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sin clasificar"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Tú determinas la importancia de estas notificaciones."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Esto es importante por los usuarios implicados."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificación de aplicación personalizada"</string>
@@ -2193,6 +2211,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Menú de acceso directo de accesibilidad en pantalla"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Acceso directo de accesibilidad"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Cerrar pantalla de notificaciones"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Cruceta: arriba"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Cruceta: abajo"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Cruceta: izquierda"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 9c9ff1b..e8696c4 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Valige juhtmevaba silumise keelamiseks."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Testrakendirežiim on lubatud"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Testrakendirežiimi keelamiseks taastage tehaseseaded."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Seeriakonsool on lubatud"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"See mõjutab toimivust. Keelamiseks kontrollige käivituslaadurit."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Katseline MTE on lubatud"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Vabastage helitugevuse klahvid. Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> sisselülitamiseks vajutage uuesti mõlemat helitugevuse klahvi ja hoidke neid 3 sekundit all."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Valige, millist funktsiooni kasutada, kui vajutate juurdepääsetavuse nuppu:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Valige, millist funktsiooni juurdepääsetavuse liigutusega (kahe sõrmega ekraanikuval alt üles pühkimine) kasutada:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Valige, millist funktsiooni juurdepääsetavuse liigutusega (kolme sõrmega ekraanikuval alt üles pühkimine) kasutada:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funktsioonide vahel vahetamiseks vajutage juurdepääsetavuse nuppu pikalt."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funktsioonide vahel vahetamiseks pühkige kahe sõrmega üles ja hoidke."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funktsioonide vahel vahetamiseks pühkige kolme sõrmega üles ja hoidke."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Suurendus"</string>
<string name="user_switched" msgid="7249833311585228097">"Praegune kasutaja <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Üleminek kasutajale <xliff:g id="NAME">%1$s</xliff:g> ..."</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Käimasolev kõne"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Sissetuleva kõne filtreerimine"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Kategoriseerimata"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Kampaaniad"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Suhtlus"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Uudised"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Soovitused"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Teie määrasite nende märguannete tähtsuse."</string>
<string name="importance_from_person" msgid="4235804979664465383">"See on tähtis osalevate inimeste tõttu."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Rakenduse kohandatud märguanne"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekraanil kuvatav juurdepääsetavuse otsetee valija"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Juurdepääsetavuse otsetee"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Loobu märguandealast"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menüü"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Meedia esitamine/peatamine"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Suunaklahvistiku ülesnool"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Suunaklahvistiku allanool"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Suunaklahvistiku vasaknool"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d2f77eb..e6a4ec7 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Hautatu hau hari gabeko arazketa desgaitzeko."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Proba-materialeko modua gaitu da"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Proba-materialaren modua desgaitzeko, berrezarri jatorrizko datuak."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serie-kontsola gaituta"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Errendimenduari eragiten dio. Desgaitzeko, joan abiarazlera."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE esperimentala gaituta dago"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu egin da."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu egin da."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Askatu bolumen-botoiak. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatzeko, eduki sakatuta berriro bi bolumen-botoiak hiru segundoz."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoia sakatzean:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Aukeratu zein eginbide erabili nahi duzun erabilerraztasun-keinuarekin (hau da, bi hatz pantailaren behealdetik gora pasatzean):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Aukeratu zein eginbide erabili nahi duzun erabilerraztasun-keinuarekin (hau da, hiru hatz pantailaren behealdetik gora pasatzean):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Eginbide batetik bestera aldatzeko, eduki sakatuta Erabilerraztasuna botoia."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Eginbide batetik bestera aldatzeko, pasatu bi hatz pantailaren behealdetik gora eta eduki sakatuta une batez."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Eginbide batetik bestera aldatzeko, pasatu hiru hatz pantailaren behealdetik gora eta eduki sakatuta une batez."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Lupa"</string>
<string name="user_switched" msgid="7249833311585228097">"Erabiltzailea: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"\"<xliff:g id="NAME">%1$s</xliff:g>\" erabiltzailera aldatzen…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Deia abian da"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Sarrerako dei bat bistaratzen"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Kategoriarik gabea"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Zuk ezarri duzu jakinarazpen hauen garrantzia."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Garrantzitsua da eragiten dien pertsonengatik."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Aplikazio-jakinarazpen pertsonalizatua"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Pantailako erabilerraztasun-lasterbideen hautatzailea"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Erabilerraztasun-lasterbidea"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Baztertu jakinarazpenen panela"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Norabide-kontrolagailuko goiko botoia"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Norabide-kontrolagailuko beheko botoia"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Norabide-kontrolagailuko ezkerreko botoia"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d761c8a..c89bba7 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -82,7 +82,7 @@
<string name="RestrictedStateContent" msgid="7693575344608618926">"شرکت مخابراتی شما موقتاً آن را خاموش کرده است"</string>
<string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"شرکت مخابراتیتان موقتاً آن را برای سیمکارت <xliff:g id="SIMNUMBER">%d</xliff:g> خاموش کرده است"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"شبکه تلفن همراه دردسترس نیست"</string>
- <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"تغییر شبکه ترجیحی را امتحان کنید. برای تغییر، ضربه بزنید."</string>
+ <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"تغییر شبکه ترجیحی را امتحان کنید. برای تغییر، تکضرب بزنید."</string>
<string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"تماس اضطراری امکانپذیر نیست"</string>
<string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"برای برقراری تماس اضطراری به شبکه تلفن همراه نیاز دارید"</string>
<string name="notification_channel_network_alert" msgid="4788053066033851841">"هشدارها"</string>
@@ -204,7 +204,7 @@
<string name="private_space_deleted_by_admin" msgid="1484365588862066939">"فضای خصوصی حذف شد"</string>
<string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"سازمان شما اجازه نمیدهد در این دستگاه مدیریتشده فضای خصوصی وجود داشته باشد."</string>
<string name="network_logging_notification_title" msgid="554983187553845004">"دستگاه مدیریت میشود"</string>
- <string name="network_logging_notification_text" msgid="1327373071132562512">"سازمانتان این دستگاه را مدیریت میکند و ممکن است ترافیک شبکه را پایش کند. برای اطلاع از جزئیات، ضربه بزنید."</string>
+ <string name="network_logging_notification_text" msgid="1327373071132562512">"سازمانتان این دستگاه را مدیریت میکند و ممکن است ترافیک شبکه را پایش کند. برای اطلاع از جزئیات، تکضرب بزنید."</string>
<string name="location_changed_notification_title" msgid="3620158742816699316">"برنامهها میتوانند به مکانتان دسترسی پیدا کنند"</string>
<string name="location_changed_notification_text" msgid="7158423339982706912">"برای کسب اطلاعات بیشتر با سرپرست فناوری اطلاعات تماس بگیرید"</string>
<string name="geofencing_service" msgid="3826902410740315456">"سرویس حصارکشی جغرافیایی"</string>
@@ -309,7 +309,7 @@
<string name="notification_channel_display" msgid="6905032605735615090">"نمایشگر"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال استفاده کردن از باتری است"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> برنامه درحال استفاده کردن از باتری هستند"</string>
- <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"برای جزئیات مربوط به مصرف باتری و داده، ضربه بزنید"</string>
+ <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"برای جزئیات مربوط به مصرف باتری و داده، تکضرب بزنید"</string>
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>، <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"حالت ایمن"</string>
<string name="android_system_label" msgid="5974767339591067210">"سیستم Android"</string>
@@ -356,7 +356,7 @@
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"کنترل درشتنمایی نمایشگر"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"موقعیت و سطح بزرگنمایی نمایشگر را کنترل کنید."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"اجرای اشارهها"</string>
- <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"میتوانید ضربه بزنید، انگشتتان را تند بکشید، انگشتانتان را به هم نزدیک یا از هم دور کنید و اشارههای دیگری اجرا کنید."</string>
+ <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"میتوانید تکضرب بزنید، انگشتتان را تند بکشید، انگشتانتان را به هم نزدیک یا از هم دور کنید و اشارههای دیگری اجرا کنید."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"اشارههای اثر انگشت"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"میتواند اشارههای اجراشده روی حسگر اثرانگشت دستگاه را ثبت کند."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"گرفتن نماگرفت"</string>
@@ -701,7 +701,7 @@
<string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"وقتی چهرهتان تشخیص داده نمیشود، مثل زمانی که نور کافی نیست، از «قفلگشایی با اثر انگشت» استفاده کنید"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"قفلگشایی با چهره"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"مشکل در «قفلگشایی با چهره»"</string>
- <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"برای حذف مدل چهرهتان ضربه بزنید، سپس چهرهتان را دوباره اضافه کنید"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"برای حذف مدل چهرهتان تکضرب بزنید، سپس چهرهتان را دوباره اضافه کنید"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"برای استفاده از «قفلگشایی با چهره»، "<b>"دسترسی به دوربین"</b>" را در «تنظیمات > حریم خصوصی» روشن کنید"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"قفلگشایی با اثر انگشت"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"امکان استفاده از حسگر اثر انگشت وجود ندارد"</string>
@@ -847,7 +847,7 @@
<string name="policydesc_resetPassword" msgid="4626419138439341851">"قفل صفحه را تغییر میدهد."</string>
<string name="policylab_forceLock" msgid="7360335502968476434">"قفل کردن صفحه"</string>
<string name="policydesc_forceLock" msgid="1008844760853899693">"نحوه و زمان قفل شدن صفحه را کنترل میکند."</string>
- <string name="policylab_wipeData" msgid="1359485247727537311">"پاک کردن تمام دادهها"</string>
+ <string name="policylab_wipeData" msgid="1359485247727537311">"پاک کردن همه دادهها"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"با انجام بازنشانی دادههای کارخانه، دادههای رایانهٔ لوحی بدون هشدار پاک میشود."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"دادههای دستگاه Android TV شما، بدون نشان داده شدن هشدار و با انجام بازنشانی دادههای کارخانه، پاک میشود."</string>
<string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"دادههای سیستم اطلاعات-سرگرمی بدون هشدار و با انجام بازنشانی دادههای کارخانه پاک میشود."</string>
@@ -986,7 +986,7 @@
<string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"PUK و پین کد جدید را تایپ کنید"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"کد PUK"</string>
<string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"پین کد جدید"</string>
- <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"برای تایپ گذرواژه ضربه بزنید"</font></string>
+ <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"برای تایپ گذرواژه تکضرب بزنید"</font></string>
<string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"برای بازکردن قفل، گذرواژه را وارد کنید"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"برای بازکردن قفل، پین را تایپ کنید"</string>
<string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"پین کد اشتباه است."</string>
@@ -1198,7 +1198,7 @@
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"فضای ذخیرهسازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راهاندازی مجدد کنید."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال اجرا است"</string>
- <string name="app_running_notification_text" msgid="5120815883400228566">"برای کسب اطلاعات بیشتر یا توقف برنامه ضربه بزنید."</string>
+ <string name="app_running_notification_text" msgid="5120815883400228566">"برای کسب اطلاعات بیشتر یا توقف برنامه تکضرب بزنید."</string>
<string name="ok" msgid="2646370155170753815">"تأیید"</string>
<string name="cancel" msgid="6908697720451760115">"لغو"</string>
<string name="yes" msgid="9069828999585032361">"تأیید"</string>
@@ -1289,15 +1289,15 @@
<string name="android_preparing_apk" msgid="589736917792300956">"آمادهسازی <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"درحال آغاز کردن برنامهها."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"درحال اتمام راهاندازی."</string>
- <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحهنمایش را خاموش میکند.\n\nهنگام راهاندازی اثر انگشت، آرام ضربه بزنید."</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحهنمایش را خاموش میکند.\n\nهنگام راهاندازی اثر انگشت، آرام تکضرب بزنید."</string>
<string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"برای اتمام راهاندازی، صفحه را خاموش کنید"</string>
<string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"خاموش کردن"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"تأیید اثر انگشت را ادامه میدهید؟"</string>
- <string name="fp_power_button_bp_message" msgid="2983163038168903393">"دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحهنمایش را خاموش میکند.\n\nبرای تأیید اثر انگشتتان، آرام ضربه بزنید."</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحهنمایش را خاموش میکند.\n\nبرای تأیید اثر انگشتتان، آرام تکضرب بزنید."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"خاموش کردن صفحه"</string>
<string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ادامه"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> در حال اجرا"</string>
- <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"برای برگشتن به بازی، ضربه بزنید"</string>
+ <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"برای برگشتن به بازی، تکضرب بزنید"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"انتخاب بازی"</string>
<string name="heavy_weight_switcher_text" msgid="6814316627367160126">"برای عملکرد بهتر، هربار فقط یکی از این بازیها را میتوان باز کرد."</string>
<string name="old_app_action" msgid="725331621042848590">"به <xliff:g id="OLD_APP">%1$s</xliff:g> برگردید"</string>
@@ -1305,19 +1305,19 @@
<string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> بدون ذخیره شدن بسته میشود"</string>
<string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> از حد مجاز حافظه فراتر رفت"</string>
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"رونوشت حافظه آزاد<xliff:g id="PROC">%1$s</xliff:g> آماده است"</string>
- <string name="dump_heap_notification_detail" msgid="8431586843001054050">"رونوشت حافظه جمعآوری شد. برای همرسانی ضربه بزنید."</string>
+ <string name="dump_heap_notification_detail" msgid="8431586843001054050">"رونوشت حافظه جمعآوری شد. برای همرسانی تکضرب بزنید."</string>
<string name="dump_heap_title" msgid="4367128917229233901">"رونوشت حافظه آزاد به اشتراک گذاشته شود؟"</string>
<string name="dump_heap_text" msgid="1692649033835719336">"فرآیند <xliff:g id="PROC">%1$s</xliff:g> از حد مجاز حافظه پردازش خود،<xliff:g id="SIZE">%2$s</xliff:g>، فراتر رفته است. رونوشت حافظه آزادی دردسترستان است که با برنامهنویس آن به اشتراک بگذارید. مراقب باشید: این رونوشت حافظه آزاد میتواند حاوی هرنوع اطلاعات شخصی شما باشد که برنامه به آن دسترسی دارد."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"فرآیند <xliff:g id="PROC">%1$s</xliff:g> از حد مجاز حافظه پردازش خود <xliff:g id="SIZE">%2$s</xliff:g> فراتر رفته است. یک رونوشت حافظه آزاد برای شما برای همرسانی دردسترس است. مواظب باشید: این رونوشت حافظه آزاد میتواند حاوی هر نوع اطلاعات شخصی شما باشد که فرآیند به آن دسترسی دارد که ممکن است شامل چیزهایی باشد که تایپ کردهاید."</string>
<string name="dump_heap_ready_text" msgid="5849618132123045516">"رونوشت حافظه آزاد فرآیند<xliff:g id="PROC">%1$s</xliff:g> برای همرسانی دردسترس شما قرار دارد. مراقب باشید: ممکن است این رونوشت حافظه آزاد حاوی اطلاعات شخصی حساس باشد که فرآیند به آن دسترسی دارد و ممکن است شامل چیزهایی باشد که تایپ کردهاید."</string>
<string name="sendText" msgid="493003724401350724">"انتخاب یک عملکرد برای نوشتار"</string>
- <string name="volume_ringtone" msgid="134784084629229029">"میزان صدای زنگ"</string>
+ <string name="volume_ringtone" msgid="134784084629229029">"صدای زنگ"</string>
<string name="volume_music" msgid="7727274216734955095">"میزان صدای رسانه"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"پخش از طریق بلوتوث"</string>
<string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"آهنگ زنگ روی بیصدا تنظیم شد"</string>
<string name="volume_call" msgid="7625321655265747433">"صدا حینِ تماس"</string>
<string name="volume_bluetooth_call" msgid="2930204618610115061">"میزان صدای تماس بلوتوث"</string>
- <string name="volume_alarm" msgid="4486241060751798448">"میزان صدای زنگ"</string>
+ <string name="volume_alarm" msgid="4486241060751798448">"صدای زنگ هشدار"</string>
<string name="volume_notification" msgid="6864412249031660057">"میزان صدای اعلان"</string>
<string name="volume_unknown" msgid="4041914008166576293">"میزان صدا"</string>
<string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"میزان صدای بلوتوث"</string>
@@ -1337,12 +1337,12 @@
<!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
<skip />
<string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> به اینترنت دسترسی ندارد"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"برای گزینهها ضربه بزنید"</string>
+ <string name="wifi_no_internet_detailed" msgid="634938444133558942">"برای گزینهها تکضرب بزنید"</string>
<string name="mobile_no_internet" msgid="4014455157529909781">"شبکه تلفن همراه به اینترنت دسترسی ندارد"</string>
<string name="other_networks_no_internet" msgid="6698711684200067033">"شبکه به اینترنت دسترسی ندارد"</string>
<string name="private_dns_broken_detailed" msgid="3709388271074611847">"سرور DNS خصوصی قابل دسترسی نیست"</string>
<string name="network_partial_connectivity" msgid="4791024923851432291">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> اتصال محدودی دارد"</string>
- <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"بههرصورت، برای اتصال ضربه بزنید"</string>
+ <string name="network_partial_connectivity_detailed" msgid="5741329444564575840">"بههرصورت، برای اتصال تکضرب بزنید"</string>
<string name="network_switch_metered" msgid="1531869544142283384">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
<string name="network_switch_metered_detail" msgid="1358296010128405906">"وقتی <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> به اینترنت دسترسی نداشته باشد، دستگاه از <xliff:g id="NEW_NETWORK">%1$s</xliff:g> استفاده میکند. ممکن است هزینههایی اعمال شود."</string>
<string name="network_switch_metered_toast" msgid="501662047275723743">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
@@ -1381,7 +1381,7 @@
<string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"برای فعال کردن سیمکارت جدیدتان، برنامه <xliff:g id="APP_NAME">%1$s</xliff:g> را بارگیری کنید"</string>
<string name="install_carrier_app_notification_button" msgid="6257740533102594290">"بارگیری برنامه"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"سیمکارت جدید جاگذاری شد"</string>
- <string name="carrier_app_notification_text" msgid="6567057546341958637">"برای تنظیم آن ضربه بزنید"</string>
+ <string name="carrier_app_notification_text" msgid="6567057546341958637">"برای تنظیم آن تکضرب بزنید"</string>
<string name="time_picker_dialog_title" msgid="9053376764985220821">"تنظیم زمان"</string>
<string name="date_picker_dialog_title" msgid="5030520449243071926">"تاریخ تنظیم"</string>
<string name="date_time_set" msgid="4603445265164486816">"تنظیم"</string>
@@ -1399,24 +1399,28 @@
<string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI ازطریق USB روشن شد"</string>
<string name="usb_uvc_notification_title" msgid="2030032862673400008">"دستگاه بهعنوان «وببین» متصل شده است"</string>
<string name="usb_accessory_notification_title" msgid="1385394660861956980">"وسیله جانبی USB متصل است"</string>
- <string name="usb_notification_message" msgid="4715163067192110676">"برای گزینههای بیشتر ضربه بزنید."</string>
- <string name="usb_power_notification_message" msgid="7284765627437897702">"درحال شارژ کردن دستگاه متصلشده. برای گزینههای بیشتر، ضربه بزنید."</string>
+ <string name="usb_notification_message" msgid="4715163067192110676">"برای گزینههای بیشتر تکضرب بزنید."</string>
+ <string name="usb_power_notification_message" msgid="7284765627437897702">"درحال شارژ کردن دستگاه متصلشده. برای گزینههای بیشتر، تکضرب بزنید."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"لوازم جانبی صوتی آنالوگ شناسایی شد"</string>
- <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"دستگاه متصلشده با این تلفن سازگار نیست. روی اطلاعات بیشتر، ضربه بزنید."</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"دستگاه متصلشده با این تلفن سازگار نیست. روی اطلاعات بیشتر، تکضرب بزنید."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"اشکالزدایی USB متصل شد"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"برای خاموش کردن اشکالزدایی USB ضربه بزنید"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"برای خاموش کردن اشکالزدایی USB تکضرب بزنید"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"انتخاب کنید تا رفع عیب USB غیرفعال شود."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"اشکالزدایی بیسیم متصل است"</string>
- <string name="adbwifi_active_notification_message" msgid="930987922852867972">"برای خاموش کردن اشکالزدایی بیسیم ضربه بزنید"</string>
+ <string name="adbwifi_active_notification_message" msgid="930987922852867972">"برای خاموش کردن اشکالزدایی بیسیم تکضرب بزنید"</string>
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"برای غیرفعال کردن اشکالزدایی بیسیم انتخاب کنید."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"«حالت مجموعه دادههای تست» فعال شد"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"برای غیرفعال کردن «حالت مجموعه دادههای تست»، بازنشانی کارخانهای کنید."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"کنسول سریال فعال است"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"عملکرد تحتتأثیر قرار گرفته است. برای غیرفعال کردن، bootloader را بررسی کنید."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE آزمایشی فعال شد"</string>
<string name="mte_override_notification_message" msgid="2441170442725738942">"شاید عملکرد و پایداری تحت تأثیر قرار بگیرند. برای غیرفعال کردن، بازراهاندازی کنید. اگر بااستفاده از arm64.memtag.bootctl فعال شده است، پیشاز بازراهاندازی مقدار آن را روی هیچکدام تنظیم کنید."</string>
<string name="usb_contaminant_detected_title" msgid="4359048603069159678">"مایعات یا خاکروبه در درگاه USB"</string>
- <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"درگاه USB بهطور خودکار غیرفعال شده است. برای اطلاعات بیشتر، ضربه بزنید."</string>
+ <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"درگاه USB بهطور خودکار غیرفعال شده است. برای اطلاعات بیشتر، تکضرب بزنید."</string>
<string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"میتوان از درگاه USB استفاده کرد"</string>
<string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"تلفن دیگر وجود مایعات یا خاکروبه را تشخیص نمیدهد."</string>
<string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"درحال گرفتن گزارش اشکال…"</string>
@@ -1430,7 +1434,7 @@
<string name="hardware" msgid="3611039921284836033">"استفاده از صفحهکلید مجازی"</string>
<string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"پیکربندی <xliff:g id="DEVICE_NAME">%s</xliff:g>"</string>
<string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"پیکربندی صفحهکلیدهای فیزیکی"</string>
- <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"برای انتخاب زبان و چیدمان ضربه بزنید"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"برای انتخاب زبان و چیدمان تکضرب بزنید"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"نمایش روی سایر برنامهها"</string>
@@ -1443,19 +1447,19 @@
<string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"درحال تجزیهوتحلیل فضای ذخیرهسازی رسانه"</string>
<string name="ext_media_new_notification_title" msgid="3517407571407687677">"<xliff:g id="NAME">%s</xliff:g> جدید"</string>
<string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> کار نمیکند"</string>
- <string name="ext_media_new_notification_message" msgid="6095403121990786986">"برای راهاندازی ضربه بزنید"</string>
+ <string name="ext_media_new_notification_message" msgid="6095403121990786986">"برای راهاندازی تکضرب بزنید"</string>
<string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"برای راهاندازی، انتخاب کنید"</string>
- <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"شاید لازم باشد دستگاه را دوباره قالببندی کنید. برای خارج کردن، ضربه بزنید."</string>
+ <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"شاید لازم باشد دستگاه را دوباره قالببندی کنید. برای خارج کردن، تکضرب بزنید."</string>
<string name="ext_media_ready_notification_message" msgid="7509496364380197369">"برای ذخیره کردن عکس، ویدیو، موسیقی و غیره"</string>
<string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"فایلهای رسانهای را مرور کنید"</string>
<string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"مشکل مرتبط با <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> کار نمیکند"</string>
- <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"برای برطرف کردن مشکل، ضربه بزنید"</string>
+ <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"برای برطرف کردن مشکل، تکضرب بزنید"</string>
<string name="ext_media_unmountable_notification_message" product="tv" msgid="3003611129979934633">"<xliff:g id="NAME">%s</xliff:g> خراب است. رفع خطا را انتخاب کنید."</string>
- <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"شاید لازم باشد دستگاه را دوباره قالببندی کنید. برای خارج کردن، ضربه بزنید."</string>
+ <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"شاید لازم باشد دستگاه را دوباره قالببندی کنید. برای خارج کردن، تکضرب بزنید."</string>
<string name="ext_media_unsupported_notification_title" msgid="3487534182861251401">"<xliff:g id="NAME">%s</xliff:g> تشخیص داده شد"</string>
<string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> کار نمیکند"</string>
- <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"برای راهاندازی ضربه بزنید."</string>
+ <string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"برای راهاندازی تکضرب بزنید."</string>
<string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"برای راهاندازی <xliff:g id="NAME">%s</xliff:g> در قالب پشتیبانیشده، انتخاب کنید."</string>
<string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"شاید لازم باشد دستگاه را دوباره قالببندی کنید"</string>
<string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> بهطور غیرمنتظره جدا شد"</string>
@@ -1500,7 +1504,7 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"به یک برنامه اجازه میدهد جهت نادیده گرفتن بهینهسازی باتری برای خود مجوز درخواست کند."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"پُرسمان همه بستهها"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"به برنامه اجازه میدهد همه بستههای نصبشده را ببیند."</string>
- <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"برای کنترل بزرگنمایی، دو بار ضربه بزنید"</string>
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"برای کنترل بزرگنمایی، دو بار تکضرب بزنید"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"افزودن ابزارک انجام نشد."</string>
<string name="ime_action_go" msgid="5536744546326495436">"برو"</string>
<string name="ime_action_search" msgid="4501435960587287668">"جستجو"</string>
@@ -1532,8 +1536,8 @@
<string name="notification_ranker_binding_label" msgid="432708245635563763">"سرویس ردهبندی اعلان"</string>
<string name="vpn_title" msgid="5906991595291514182">"VPN فعال شد"</string>
<string name="vpn_title_long" msgid="6834144390504619998">"VPN را <xliff:g id="APP">%s</xliff:g> فعال کرده است"</string>
- <string name="vpn_text" msgid="2275388920267251078">"برای مدیریت شبکه ضربه بزنید."</string>
- <string name="vpn_text_long" msgid="278540576806169831">"به <xliff:g id="SESSION">%s</xliff:g> متصل شد. برای مدیریت شبکه ضربه بزنید."</string>
+ <string name="vpn_text" msgid="2275388920267251078">"برای مدیریت شبکه تکضرب بزنید."</string>
+ <string name="vpn_text_long" msgid="278540576806169831">"به <xliff:g id="SESSION">%s</xliff:g> متصل شد. برای مدیریت شبکه تکضرب بزنید."</string>
<string name="vpn_lockdown_connecting" msgid="6096725311950342607">"درحال اتصال به VPN همیشه روشن…"</string>
<string name="vpn_lockdown_connected" msgid="2853127976590658469">"VPN همیشه روشن متصل شد"</string>
<string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"از «VPN همیشه روشن» قطع شد"</string>
@@ -1544,7 +1548,7 @@
<string name="reset" msgid="3865826612628171429">"بازنشانی"</string>
<string name="submit" msgid="862795280643405865">"ارسال"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"برنامه رانندگی درحال اجرا است"</string>
- <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"برای خروج از برنامه رانندگی ضربه بزنید."</string>
+ <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"برای خروج از برنامه رانندگی تکضرب بزنید."</string>
<string name="back_button_label" msgid="4078224038025043387">"برگشت"</string>
<string name="next_button_label" msgid="6040209156399907780">"بعدی"</string>
<string name="skip_button_label" msgid="3566599811326688389">"رد شدن"</string>
@@ -1623,7 +1627,7 @@
<string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"بیشاز حدمجاز داده Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"مصرف داده شما <xliff:g id="SIZE">%s</xliff:g> از حداکثر مجاز بیشتر شده است"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"داده پسزمینه محدود شد"</string>
- <string name="data_usage_restricted_body" msgid="5338694433686077733">"برای برداشتن محدودیت ضربه بزنید."</string>
+ <string name="data_usage_restricted_body" msgid="5338694433686077733">"برای برداشتن محدودیت تکضرب بزنید."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"مصرف بالای داده تلفن همراه"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"برنامههای شما بیش از معمول داده مصرف کردهاند"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> بیش از معمول داده مصرف کرده است"</string>
@@ -1739,7 +1743,7 @@
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"مجاز نبودن"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"حذف نصب"</string>
<string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"پاسخ شما تأیید نشد زیرا یک برنامه درخواست اجازه را مسدود کرده است."</string>
- <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن ضربه بزنید:"</string>
+ <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن تکضرب بزنید:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"انتخاب ویژگیهای موردنظر برای استفاده با دکمه دسترسپذیری"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"انتخاب ویژگیهای موردنظر برای استفاده با میانبر کلید میزان صدا"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> خاموش شده است"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"کلیدهای میزان صدا را رها کنید. برای روشن کردن <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید میزان صدا را مجدداً بهمدت ۳ ثانیه فشار دهید و نگه دارید."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ویژگی را انتخاب کنید که هنگام ضربه زدن روی دکمه دسترسپذیری استفاده میشود:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ویژگی را برای استفاده با اشاره دسترسپذیری انتخاب کنید (با دو انگشت صفحه را از پایین تند بهبالا بکشید):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ویزگی را برای استفاده با اشاره دسترسپذیری انتخاب کنید (با سه انگشت صفحه را از پایین تند بهبالا بکشید):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"برای جابهجایی بین ویژگیها، دکمه دسترسپذیری را لمس کنید و نگه دارید."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"برای جابهجایی بین ویژگیها، با دو انگشت صفحه را تند بهبالا بکشید و نگه دارید."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"برای جابهجایی بین ویژگیها، با سه انگشت صفحه را تند بهبالا بکشید و نگه دارید."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"درشتنمایی"</string>
<string name="user_switched" msgid="7249833311585228097">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"در حالت تغییر به <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1871,7 +1881,7 @@
<string name="reason_unknown" msgid="5599739807581133337">"نامعلوم"</string>
<string name="reason_service_unavailable" msgid="5288405248063804713">"سرویس چاپ فعال نشده است"</string>
<string name="print_service_installed_title" msgid="6134880817336942482">"سرویس <xliff:g id="NAME">%s</xliff:g> نصب شد"</string>
- <string name="print_service_installed_message" msgid="7005672469916968131">"برای فعال کردن ضربه بزنید"</string>
+ <string name="print_service_installed_message" msgid="7005672469916968131">"برای فعال کردن تکضرب بزنید"</string>
<string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"پین سرپرست سیستم را وارد کنید"</string>
<string name="restr_pin_enter_pin" msgid="373139384161304555">"پین را وارد کنید"</string>
<string name="restr_pin_incorrect" msgid="3861383632940852496">"نادرست"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"تماس درحال انجام"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"درحال غربال کردن تماس ورودی"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"دستهبندینشده"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"تبلیغات ویژه"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"اجتماعی"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"اخبار"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"توصیهها"</string>
<string name="importance_from_user" msgid="2782756722448800447">"شما اهمیت این اعلانها را تنظیم میکنید."</string>
<string name="importance_from_person" msgid="4235804979664465383">"به دلیل افراد درگیر مهم است."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"اعلان برنامه سفارشی"</string>
@@ -2032,9 +2046,9 @@
<string name="new_sms_notification_content" msgid="3197949934153460639">"برای مشاهده، برنامه پیامک را باز کنید"</string>
<string name="profile_encrypted_title" msgid="9001208667521266472">"برخی از عملکردها ممکن است محدود باشند"</string>
<string name="profile_encrypted_detail" msgid="5279730442756849055">"نمایه کاری قفل است"</string>
- <string name="profile_encrypted_message" msgid="1128512616293157802">"برای باز کردن قفل ضربه بزنید"</string>
+ <string name="profile_encrypted_message" msgid="1128512616293157802">"برای باز کردن قفل تکضرب بزنید"</string>
<string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"به <xliff:g id="PRODUCT_NAME">%1$s</xliff:g> متصل شد"</string>
- <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"برای دیدن فایلها، ضربه بزنید"</string>
+ <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"برای دیدن فایلها، تکضرب بزنید"</string>
<string name="pin_target" msgid="8036028973110156895">"سنجاق کردن"</string>
<string name="pin_specific_target" msgid="7824671240625957415">"سنجاق کردن <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"برداشتن سنجاق"</string>
@@ -2122,11 +2136,11 @@
<string name="notification_channel_system_changes" msgid="2462010596920209678">"تغییرات سیستم"</string>
<string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"مزاحم نشوید"</string>
<string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"جدید: «مزاحم نشوید» اعلانها را پنهان میکند"</string>
- <string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"برای اطلاعات بیشتر و تغییر دادن، ضربه بزنید."</string>
+ <string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"برای اطلاعات بیشتر و تغییر دادن، تکضرب بزنید."</string>
<string name="zen_upgrade_notification_title" msgid="8198167698095298717">"«مزاحم نشوید» تغییر کرده است"</string>
- <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"برای بررسی موارد مسدودشده ضربه بزنید."</string>
+ <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"برای بررسی موارد مسدودشده تکضرب بزنید."</string>
<string name="review_notification_settings_title" msgid="5102557424459810820">"مرور تنظیمات اعلان"</string>
- <string name="review_notification_settings_text" msgid="5916244866751849279">"از Android نسخه ۱۳ به بعد، برنامههایی که نصب میکنید برای ارسال اعلان به اجازه شما نیاز دارند. برای تغییر دادن این اجازه در برنامههای موجود، ضربه بزنید."</string>
+ <string name="review_notification_settings_text" msgid="5916244866751849279">"از Android نسخه ۱۳ به بعد، برنامههایی که نصب میکنید برای ارسال اعلان به اجازه شما نیاز دارند. برای تغییر دادن این اجازه در برنامههای موجود، تکضرب بزنید."</string>
<string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"بعداً یادآوری شود"</string>
<string name="review_notification_settings_dismiss" msgid="4160916504616428294">"رد شدن"</string>
<string name="notification_app_name_system" msgid="3045196791746735601">"سیستم"</string>
@@ -2135,10 +2149,10 @@
<string name="notification_appops_microphone_active" msgid="581333393214739332">"میکروفون"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"نمایش روی برنامههای دیگر در صفحهنمایش"</string>
<string name="notification_feedback_indicator" msgid="663476517711323016">"ارائه بازخورد"</string>
- <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"این اعلان به «پیشفرض» ارتقا داده شد. برای ارائه بازخورد، ضربه بزنید."</string>
- <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"این اعلان به «بیصدا» تنزل داده شد. برای ارائه بازخورد، ضربه بزنید."</string>
- <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"این اعلان در رتبه بالاتری قرار گرفت. برای ارائه بازخورد، ضربه بزنید."</string>
- <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"این اعلان در رتبه پایینتری قرار گرفت. برای ارائه بازخورد، ضربه بزنید."</string>
+ <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"این اعلان به «پیشفرض» ارتقا داده شد. برای ارائه بازخورد، تکضرب بزنید."</string>
+ <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"این اعلان به «بیصدا» تنزل داده شد. برای ارائه بازخورد، تکضرب بزنید."</string>
+ <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"این اعلان در رتبه بالاتری قرار گرفت. برای ارائه بازخورد، تکضرب بزنید."</string>
+ <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"این اعلان در رتبه پایینتری قرار گرفت. برای ارائه بازخورد، تکضرب بزنید."</string>
<string name="nas_upgrade_notification_title" msgid="8436359459300146555">"اعلانهای بهبودیافته"</string>
<string name="nas_upgrade_notification_content" msgid="5157550369837103337">"اکنون پاسخها و کنشهای پیشنهادی ازطریق اعلانهای بهبودیافته ارائه میشوند. «اعلانهای تطبیقی Android» دیگر پشتیبانی نمیشوند."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"تأیید"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"انتخابگر میانبر دسترسپذیری روی صفحه"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"میانبر دسترسیپذیری"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"رد کردن کشوی اعلانات"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"منو"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"پخش/موقتاً متوقف کردن رسانه"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"پد کنترل بالا"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"پد کنترل پایین"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"پد کنترل چپ"</string>
@@ -2353,14 +2369,14 @@
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"نماد برنامه"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"تصویر نمانامسازی برنامه"</string>
<string name="view_and_control_notification_title" msgid="4300765399209912240">"بررسی تنظیمات دسترسی"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> میتواند صفحهنمایش شما را مشاهده و کنترل کند. برای مرور، ضربه بزنید."</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> میتواند صفحهنمایش شما را مشاهده و کنترل کند. برای مرور، تکضرب بزنید."</string>
<string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> ترجمه شد."</string>
<string name="ui_translation_accessibility_translation_finished" msgid="3057830947610088465">"پیام از <xliff:g id="FROM_LANGUAGE">%1$s</xliff:g> به <xliff:g id="TO_LANGUAGE">%2$s</xliff:g> ترجمه شد."</string>
<string name="notification_channel_abusive_bg_apps" msgid="6092140213264920355">"فعالیت در پسزمینه"</string>
<string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"برنامهای شارژ باتری را خالی میکند"</string>
<string name="notification_title_long_running_fgs" msgid="8170284286477131587">"یکی از برنامهها همچنان فعال است"</string>
- <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> در پسزمینه درحال اجرا است. برای مدیریت مصرف باتری ضربه بزنید."</string>
- <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> ممکن است بر عمر باتری تأثیر بگذارد. برای مرور برنامههای فعال، ضربه بزنید."</string>
+ <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> در پسزمینه درحال اجرا است. برای مدیریت مصرف باتری تکضرب بزنید."</string>
+ <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> ممکن است بر عمر باتری تأثیر بگذارد. برای مرور برنامههای فعال، تکضرب بزنید."</string>
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"بررسی برنامههای فعال"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"از <xliff:g id="DEVICE">%1$s</xliff:g> به دوربین تلفن دسترسی ندارید"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"نمیتوان از <xliff:g id="DEVICE">%1$s</xliff:g> شما به دوربین رایانه لوحی دسترسی داشت"</string>
@@ -2391,12 +2407,12 @@
<string name="device_state_notification_settings_button" msgid="691937505741872749">"رفتن به تنظیمات"</string>
<string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"خاموش کردن"</string>
<string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> پیکربندی شد"</string>
- <string name="keyboard_layout_notification_one_selected_message" msgid="4314216053129257197">"جانمایی صفحهکلید روی <xliff:g id="LAYOUT_1">%s</xliff:g> تنظیم شد. برای تغییر دادن، ضربه بزنید."</string>
- <string name="keyboard_layout_notification_two_selected_message" msgid="1876349944065922950">"جانمایی صفحهکلید روی <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g> تنظیم شد. برای تغییر دادن، ضربه بزنید."</string>
- <string name="keyboard_layout_notification_three_selected_message" msgid="280734264593115419">"جانمایی صفحهکلید روی <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g>، <xliff:g id="LAYOUT_3">%3$s</xliff:g> تنظیم شد. برای تغییر دادن، ضربه بزنید."</string>
- <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"جانمایی صفحهکلید چنین تنظیم شد: <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g>، <xliff:g id="LAYOUT_3">%3$s</xliff:g>… برای تغییر ضربه بزنید"</string>
+ <string name="keyboard_layout_notification_one_selected_message" msgid="4314216053129257197">"جانمایی صفحهکلید روی <xliff:g id="LAYOUT_1">%s</xliff:g> تنظیم شد. برای تغییر دادن، تکضرب بزنید."</string>
+ <string name="keyboard_layout_notification_two_selected_message" msgid="1876349944065922950">"جانمایی صفحهکلید روی <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g> تنظیم شد. برای تغییر دادن، تکضرب بزنید."</string>
+ <string name="keyboard_layout_notification_three_selected_message" msgid="280734264593115419">"جانمایی صفحهکلید روی <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g>، <xliff:g id="LAYOUT_3">%3$s</xliff:g> تنظیم شد. برای تغییر دادن، تکضرب بزنید."</string>
+ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"جانمایی صفحهکلید چنین تنظیم شد: <xliff:g id="LAYOUT_1">%1$s</xliff:g>، <xliff:g id="LAYOUT_2">%2$s</xliff:g>، <xliff:g id="LAYOUT_3">%3$s</xliff:g>… برای تغییر تکضرب بزنید"</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"صفحهکلیدهای فیزیکی پیکربندی شدند"</string>
- <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"برای مشاهده صفحهکلیدها ضربه بزنید"</string>
+ <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"برای مشاهده صفحهکلیدها تکضرب بزنید"</string>
<string name="profile_label_private" msgid="6463418670715290696">"خصوصی"</string>
<string name="profile_label_clone" msgid="769106052210954285">"همسانهسازی"</string>
<string name="profile_label_work" msgid="3495359133038584618">"کار"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 9a7bfff..6a12f3c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Poista langaton virheenkorjaus käytöstä valitsemalla tämä."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Testikehystila käytössä"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Palauta tehdasasetukset, niin voit poistaa testikehystilan käytöstä."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Sarjakonsoli käytössä"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Tämä vaikuttaa suorituskykyyn. Jos haluat poistaa toiminnon käytöstä, tarkista käynnistysohjelma."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Kokeellinen MTE käytössä"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Vapauta äänenvoimakkuuspainikkeet. Laita <xliff:g id="SERVICE_NAME">%1$s</xliff:g> päälle painamalla äänenvoimakkuuspainikkeita uudelleen kolmen sekunnin ajan."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Valitse ominaisuus, jonka esteettömyyspainike aktivoi:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Valitse ominaisuus, jota käytetään esteettömyyseleellä (pyyhkäise näytön alalaidasta ylös kahdella sormella):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Valitse ominaisuus, jota käytetään esteettömyyseleellä (pyyhkäise näytön alalaidasta ylös kolmella sormella):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Vaihda ominaisuuksien välillä painamalla esteettömyyspainiketta pitkään."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Vaihda ominaisuuksien välillä pyyhkäisemällä ylös kahdella sormella ja koskettamalla pitkään."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Vaihda ominaisuuksien välillä pyyhkäisemällä ylös kolmella sormella ja koskettamalla pitkään."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Suurennus"</string>
<string name="user_switched" msgid="7249833311585228097">"Nykyinen käyttäjä: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Vaihdetaan käyttäjään <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Käynnissä oleva puhelu"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Seulotaan saapuvaa puhelua"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Luokittelematon"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Voit valita näiden ilmoitusten tärkeyden."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Tämä on tärkeää siihen liittyvien ihmisten perusteella."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Oma sovellusilmoitus"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Näytöllä näkyvän esteettömyyspainikkeen valitsin"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Saavutettavuuspainike"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Sulje ilmoitusalue"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Suuntanäppäimistö: ylös-painike"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Suuntanäppäimisto: alas-painike"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Suuntanäppäimistö: vasen painike"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index cc56526..afd5c9a 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Sélectionnez cette option pour désactiver le débogage sans fil."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Mode Logiciel de test activé"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Effectuez une réinitialisation pour désactiver le mode Logiciel de test."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"La console série est activée"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"La performance est réduite. Pour désactiver cette fonction, vérifier le programme d\'amorçage."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Extension MTE expérimentale activée"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Relâchez les touches de volume. Pour activer <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, maintenez les deux touches de volume enfoncées pendant 3 secondes."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Choisissez une fonctionnalité à utiliser lorsque vous touchez le bouton d\'accessibilité :"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choisissez une fonctionnalité à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer l\'écran de bas en haut avec deux doigts) :"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Choisissez une fonctionnalité à utiliser lorsque vous utilisez le geste d\'accessibilité (balayer l\'écran de bas en haut avec trois doigts) :"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Pour basculer entre les fonctionnalités, maintenez le doigt sur le bouton d\'accessibilité."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Pour basculer entre les fonctionnalités, balayez l\'écran vers le haut avec deux doigts et maintenez-les-y."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Pour basculer entre les fonctionnalités, balayez l\'écran vers le haut avec trois doigts et maintenez-les-y."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Agrandissement"</string>
<string name="user_switched" msgid="7249833311585228097">"Utilisateur actuel : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="user_switching_message" msgid="1912993630661332336">"Passage au profil : <xliff:g id="NAME">%1$s</xliff:g> en cours…"</string>
@@ -1977,6 +1987,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Appel en cours"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrer un appel entrant"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sans catégorie"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Vous définissez l\'importance de ces notifications."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ces notifications sont importantes en raison des participants."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notification d\'application personnalisée"</string>
@@ -2193,6 +2211,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Sélecteur de raccourci d\'accessibilité à l\'écran"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Raccourci d\'accessibilité"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ignorer le volet de notification"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Pavé directionnel – haut"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Pavé directionnel – bas"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pavé directionnel – gauche"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f1a81a3..35a866c 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Sélectionnez cette option pour désactiver le débogage sans fil."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Mode Atelier de test activé"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Rétablissez la configuration d\'usine pour désactiver le mode Atelier de test."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Console série activée"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Les performances sont affectées. Pour désactiver la console série, vérifiez le bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE expérimentale activée"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Relâchez les boutons de volume. Pour activer <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, appuyez de nouveau sur les deux boutons de volume pendant trois secondes."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Choisissez une fonctionnalité à utiliser lorsque vous appuyez sur le bouton Accessibilité :"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Choisissez une fonctionnalité à utiliser avec le geste d\'accessibilité (balayez l\'écran de bas en haut avec deux doigts) :"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Choisissez une fonctionnalité à utiliser avec le geste d\'accessibilité (balayer l\'écran de bas en haut avec trois doigts) :"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Pour changer de fonctionnalité, appuyez de manière prolongée sur le bouton Accessibilité."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Pour changer de fonctionnement, balayez l\'écran vers le haut avec deux doigts et appuyez de manière prolongée."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Pour changer de fonctionnalité, balayez l\'écran vers le haut avec trois doigts et appuyez de manière prolongée."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Agrandissement"</string>
<string name="user_switched" msgid="7249833311585228097">"Utilisateur actuel : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="user_switching_message" msgid="1912993630661332336">"Passage à <xliff:g id="NAME">%1$s</xliff:g>..."</string>
@@ -1977,6 +1987,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Appel en cours"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrage d\'un appel entrant"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sans catégorie"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Vous définissez l\'importance de ces notifications."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ces notifications sont importantes en raison des participants."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notification d\'application personnalisée"</string>
@@ -2193,6 +2211,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Outil de sélection des raccourcis d\'accessibilité à l\'écran"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Raccourci d\'accessibilité"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Fermer le volet des notifications"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Pavé directionnel - Haut"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Pavé directionnel - Bas"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pavé directionnel - Gauche"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index c29d888..3f3634a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -642,7 +642,7 @@
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite que a aplicación lea as localizacións da túa colección multimedia."</string>
<string name="biometric_app_setting_name" msgid="3339209978734534457">"Utilizar desbloqueo biométrico"</string>
<string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Utilizar desbloqueo biométrico ou credencial do dispositivo"</string>
- <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica que es ti"</string>
+ <string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifica a túa identidade"</string>
<string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Para continuar, utiliza o desbloqueo biométrico"</string>
<string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Para continuar, utiliza o desbloqueo biométrico ou o bloqueo de pantalla"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"O hardware biométrico non está dispoñible"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecciona para desactivar a depuración sen fíos."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Activouse o modo de axente de proba"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Restablece a configuración de fábrica para desactivar o modo de axente de proba."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"A consola de serie está activada"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"O rendemento vese afectado. Para desactivar a consola, comproba o cargador de arranque."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"A MTE experimental está activada"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume premidas. Activouse o servizo <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Desactivouse <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solta as teclas de volume. Para activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantenas premidas de novo durante 3 segundos."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Escolle a función que queres utilizar cando toques o botón Accesibilidade:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Escolle a función que queres usar co xesto de accesibilidade (pasa dous dedos cara arriba desde a parte inferior da pantalla):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Escolle a función que queres usar co xesto de accesibilidade (pasa tres dedos cara arriba desde a parte inferior da pantalla):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para cambiar de función, mantén premido o botón Accesibilidade."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para cambiar de función, pasa dous dedos cara arriba pola pantalla e mantén premido."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para cambiar de función, pasa tres dedos cara arriba pola pantalla e mantén premido."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ampliación"</string>
<string name="user_switched" msgid="7249833311585228097">"Usuario actual <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Cambiando a <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada en curso"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando chamada entrante"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sen clasificar"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promocións"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Novidades"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recomendacións"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Ti defines a importancia destas notificacións."</string>
<string name="importance_from_person" msgid="4235804979664465383">"É importante polas persoas involucradas."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificación de aplicación personalizada"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selector de atallos de accesibilidade en pantalla"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Atallo de accesibilidade"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ignorar panel despregable"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menú"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Reproducir/pór en pausa contido multimedia"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Cruceta: arriba"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Cruceta: abaixo"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Cruceta: esquerda"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4d350a0..b391866 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"વાયરલેસ ડિબગીંગ બંધ કરવા માટે પસંદ કરો."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"ટેસ્ટ હાર્નેસ મોડ ચાલુ કર્યો"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"ટેસ્ટ હાર્નેસ મોડ બંધ કરવા માટે ફૅક્ટરી રીસેટ કરો."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"સિરીયલ કન્સોલ ચાલુ થયો"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"કાર્યપ્રદર્શનને અસર થાય છે. બંધ કરવા માટે, બૂટલોડર ચેક કરો."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"પ્રયોગાત્મક MTE ચાલુ કર્યું"</string>
@@ -1433,7 +1437,7 @@
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"ભાષા અને લેઆઉટ પસંદ કરવા માટે ટૅપ કરો"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"અન્ય ઍપથી ઉપર બતાવો"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"અન્ય ઍપની ઉપર ડિસ્પ્લે કરો"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> અન્ય ઍપ્લિકેશનોની ઉપર પ્રદર્શિત થઈ રહ્યું છે"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> અન્ય ઍપ્લિકેશનો પર દેખાઈ છે"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"જો તમે નથી ઇચ્છતા કે <xliff:g id="NAME">%s</xliff:g> આ સુવિધાનો ઉપયોગ કરે, તો સેટિંગ ખોલવા માટે ટૅપ કરો અને તેને બંધ કરો."</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"વૉલ્યૂમ કી છોડી દો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ને ચાલુ કરવા માટે, 3 સેકન્ડ માટે બન્ને વૉલ્યૂમ કીને ફરીથી દબાવી રાખો."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"જ્યારે તમે ઍક્સેસિબિલિટી બટન પર ટૅપ કરો, ત્યારે ઉપયોગ કરવાની સુવિધા પસંદ કરો:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ઍક્સેસિબિલિટી સંકેત સાથે ઉપયોગ કરવાની સુવિધા પસંદ કરો (બે આંગળીઓ વડે સ્ક્રીનના નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરવા માટે):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ઍક્સેસિબિલિટી સંકેત સાથે ઉપયોગ કરવાની સુવિધા પસંદ કરો (ત્રણ આંગળીઓ વડે સ્ક્રીનના નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરવા માટે):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"કોઈ એક સુવિધાથી બીજી સુવિધા પર સ્વિચ કરવા માટે, ઍક્સેસિબિલિટી બટનને ટચ કરીને થોડીવાર દબાવી રાખો."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"કોઈ એક સુવિધાથી બીજી સુવિધા પર સ્વિચ કરવા માટે, બે આંગળીઓ વડે સ્ક્રીનની ઉપરની તરફ સ્વાઇપ કરીને દબાવી રાખો."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"કોઈ એક સુવિધાથી બીજી સુવિધા પર સ્વિચ કરવા માટે, ત્રણ આંગળીઓ વડે સ્ક્રીનની ઉપરની તરફ સ્વાઇપ કરીને દબાવી રાખો."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"મોટું કરવું"</string>
<string name="user_switched" msgid="7249833311585228097">"વર્તમાન વપરાશકર્તા <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> પર સ્વિચ કરી રહ્યાં છે…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"ચાલુ કૉલ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ઇનકમિંગ કૉલનું સ્ક્રીનિંગ થાય છે"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"અવર્ગીકૃત"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"પ્રમોશન"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"સામાજિક"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ન્યૂઝ"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"સુઝાવો"</string>
<string name="importance_from_user" msgid="2782756722448800447">"તમે આ સૂચનાઓનું મહત્વ સેટ કર્યું છે."</string>
<string name="importance_from_person" msgid="4235804979664465383">"શામેલ થયેલ લોકોને કારણે આ મહત્વપૂર્ણ છે."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ઍપનું કસ્ટમ નોટિફિકેશન"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"સ્ક્રીન પરના ઍક્સેસિબિલિટી શૉર્ટકટના પસંદકર્તા"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ઍક્સેસિબિલિટી શૉર્ટકટ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"નોટિફિકેશન શેડ છોડી દો"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"મેનૂ"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"મીડિયા ચલાવો/થોભાવો"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"ડી-પૅડ ઉપર"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"ડી-પૅડ નીચે"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ડી-પૅડ ડાબે"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 39e6625..6db2ee2 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"वॉयरलेस डीबगिंग की सुविधा बंद करने के लिए चुनें."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"टेस्ट हार्नेस मोड चालू किया गया"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"टेस्ट हार्नेस मोड बंद करने के लिए फ़ैक्ट्री रीसेट करें."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"सीरियल कंसोल को चालू करें"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"परफ़ॉर्मेंस पर असर पड़ा है. बंद करने के लिए बूटलोडर चुनें."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"आज़माने के लिए एमटीई चालू है"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू कर दिया गया."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद कर दिया गया."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"आवाज़ बटन को छोड़ें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> की सुविधा चालू करने के लिए, आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"सुलभता बटन पर टैप करके, इस्तेमाल करने के लिए सुविधा चुनें:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"सुलभता वाले हाथ के जेस्चर (हाव-भाव) के साथ इस्तेमाल करने के लिए सुविधा चुनें (दो उंगलियों से स्क्रीन पर सबसे नीचे से ऊपर की ओर स्वाइप करें):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"सुलभता वाले हाथ के जेस्चर (हाव-भाव) के साथ इस्तेमाल करने के लिए सुविधा चुनें (तीन उंगलियों से स्क्रीन पर सबसे नीचे से ऊपर की ओर स्वाइप करें):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एक सुविधा से दूसरी सुविधा पर जाने के लिए, सुलभता बटन दबाकर रखें."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"एक सुविधा से दूसरी सुविधा पर जाने के लिए, स्क्रीन पर दो उंगलियों से ऊपर की ओर स्वाइप करें और थोड़ी देर तक उंगलियां स्क्रीन पर रखे रहें."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एक सुविधा से दूसरी सुविधा पर जाने के लिए, स्क्रीन पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और थोड़ी देर तक उंगलियां स्क्रीन पर रखे रहें."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ज़ूम करने की सुविधा"</string>
<string name="user_switched" msgid="7249833311585228097">"मौजूदा उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> पर स्विच किया जा रहा है…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"पहले से जारी कॉल"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"इनकमिंग कॉल को स्क्रीन किया जा रहा है"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"बिना किसी श्रेणी के"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"प्रमोशन"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"सोशल"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"खबरें"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"सुझाव"</string>
<string name="importance_from_user" msgid="2782756722448800447">"आपने इन सूचनाओं की अहमियत सेट की है."</string>
<string name="importance_from_person" msgid="4235804979664465383">"यह मौजूद व्यक्तियों के कारण महत्वपूर्ण है."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ऐप्लिकेशन की सूचना पसंद के मुताबिक बनाएं"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"स्क्रीन पर दिखने वाले सुलभता के शॉर्टकट को चुनने का मेन्यू"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"सुलभता का शॉर्टकट"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"नोटिफ़िकेशन शेड खारिज करें"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"मेन्यू"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"मीडिया चलाएं/रोकें"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"डी-पैड का ऊपर वाला बटन"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"डी-पैड का नीचे वाला बटन"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"डी-पैड का बाईं ओर वाला बटन"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 08ea4a2..611dc46 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Odaberite da biste onemogućili bežično otklanjanje pogrešaka."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Omogućen je način testnog okvira"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Vratite na tvorničke postavke da biste onemogućili način testnog okvira."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola omogućena"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Izvedba je otežana. Provjerite pokretač operativnog sustava da biste onemogućili konzolu."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Omogućen je eksperimentalni MTE"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za glasnoću. Uključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za glasnoću. Isključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Pustite tipke za glasnoću. Da biste uključili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ponovo pritisnite i zadržite obje tipke za glasnoću tri sekunde."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Odaberite značajku koju ćete upotrebljavati kada dodirnete gumb za Pristupačnost:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Odaberite za koju će se značajku upotrebljavati pokret pristupačnosti (prelazak s dva prsta prema gore od dna zaslona):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Odaberite za koju će se značajku upotrebljavati pokret pristupačnosti (prelazak s tri prsta prema gore od dna zaslona):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Da biste prešli na neku drugu značajku, dodirnite i zadržite gumb za Pristupačnost."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Da biste prešli na neku drugu značajku, prijeđite s dva prsta prema gore i zadržite."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Da biste prešli na neku drugu značajku, prijeđite s tri prsta prema gore i zadržite."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Povećavanje"</string>
<string name="user_switched" msgid="7249833311585228097">"Trenutačni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Prebacivanje na korisnika <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv u tijeku"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtriranje dolaznog poziva"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Nema kategorije"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promocije"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Društvene mreže"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Vijesti"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Preporuke"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Postavili ste važnost tih obavijesti."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Važno je zbog uključenih osoba."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Prilagođena obavijest aplikacije"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Alat za odabir prečaca pristupačnosti na zaslonu"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Prečac pristupačnosti"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Odbacivanje zaslona obavijesti"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Izbornik"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Reproduciraj/pauziraj medijske sadržaje"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Gore na plohi za smjerove"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dolje na plohi za smjerove"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Lijevo na plohi za smjerove"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 729fad9..dbbba59 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Válassza ezt a vezeték nélküli hibakeresés letiltásához."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Tesztelési alapkörnyezet mód engedélyezve"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"A Tesztelési alapkörnyezet mód kikapcsolásához állítsa vissza a gyári beállításokat."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Soros konzol engedélyezve"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Ez hatással van a teljesítményre. A letiltáshoz ellenőrizze a rendszerindítót."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Kísérleti MTE engedélyezve"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Engedje fel a hangerőszabályzó gombokat. A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolásához tartsa újra lenyomva a hangerőszabályzó gombokat három másodpercig."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Válassza ki a Kisegítő lehetőségek gombra koppintáskor használni kívánt funkciót:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Válassza ki a kisegítő kézmozdulattal (felfelé csúsztatás két ujjal a képernyő aljáról) használni kívánt funkciót:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Válassza ki a kisegítő kézmozdulattal (felfelé csúsztatás három ujjal a képernyő aljáról) használni kívánt funkciót:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"A funkciók közötti váltáshoz tartsa lenyomva a Kisegítő lehetőségek gombot."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"A funkciók közötti váltáshoz csúsztassa felfelé két ujját, és ne emelje fel őket a képernyőről."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"A funkciók közötti váltáshoz csúsztassa felfelé három ujját, és ne emelje fel őket a képernyőről."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Nagyítás"</string>
<string name="user_switched" msgid="7249833311585228097">"<xliff:g id="NAME">%1$s</xliff:g> az aktuális felhasználó."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Átváltás erre: <xliff:g id="NAME">%1$s</xliff:g>..."</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Hívás folyamatban"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Bejövő hívás szűrése"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Nincs kategóriába sorolva"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Ön állította be ezen értesítések fontossági szintjét."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ez az üzenet a résztvevők miatt fontos."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Egyéni alkalmazásértesítés"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Képernyőn megjelenő kisegítő lehetőségekre vonatkozó parancsválasztó"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Kisegítő lehetőségek gyorsparancsa"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Értesítési felület bezárása"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"D-pad – fel"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"D-pad – le"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad – balra"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index c3eec0f..c690b29 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Սեղմեք՝ անլար վրիպազերծումն անջատելու համար:"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Թեստային ռեժիմը միացված է"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Թեստային ռեժիմն անջատելու համար զրոյացրեք կարգավորումները։"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Սերիական վահանակը միացված է"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Աշխատանքի արդյունավետությունը նվազում է։ Վահանակն անջատելու համար ստուգեք օպերացիոն համակարգի բեռնիչը։"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Միացված է փորձնական MTE գործառույթը"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Բաց թողեք ձայնի ուժգնության կոճակները։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացնելու համար սեղմեք և 3 վայրկյան պահեք ձայնի ուժգնության երկու կոճակը։"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Ընտրեք գործառույթը, որը կգործարկվի «Հատուկ գործառույթներ» կոճակին հպելու դեպքում՝"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Ընտրեք գործառույթը, որը կգործարկվի հատուկ գործառույթների ժեստը (երկու մատը էկրանի ներքևից սահեցրեք վերև) անելու դեպքում՝"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Ընտրեք գործառույթը, որը կգործարկվի հատուկ գործառույթների ժեստը (երեք մատը էկրանի ներքևից սահեցրեք վերև) անելու դեպքում՝"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Մի գործառույթից մյուսին անցնելու համար հպեք «Հատուկ գործառույթներ» կոճակին և պահեք։"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Մի գործառույթից մյուսին անցնելու համար երկու մատը սահեցրեք վերև և պահեք։"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Մի գործառույթից մյուսին անցնելու համար երեք մատը սահեցրեք վերև և պահեք։"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Խոշորացում"</string>
<string name="user_switched" msgid="7249833311585228097">"Ներկայիս օգտատերը <xliff:g id="NAME">%1$s</xliff:g>:"</string>
<string name="user_switching_message" msgid="1912993630661332336">"Անցում <xliff:g id="NAME">%1$s</xliff:g> պրոֆիլին..."</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ընթացիկ զանգ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Մուտքային զանգի զտում"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Չդասակարգված"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Դուք սահմանել եք այս ծանուցումների կարևորությունը:"</string>
<string name="importance_from_person" msgid="4235804979664465383">"Կարևոր է, քանի որ որոշակի մարդիկ են ներգրավված:"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Հատուկ հավելվածի ծանուցում"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Հատուկ գործառույթների դյուրանցման ընտրիչ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Հատուկ գործառույթների դյուրանցում"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Փակել ծանուցումների վահանակը"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad-ի «Վերև» կոճակ"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad-ի «Ներքև» կոճակ"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad-ի «Ձախ» կոճակ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 34a52e4..1b3fc0e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Pilih untuk menonaktifkan proses debug nirkabel."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Mode Tes Otomatis diaktifkan"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Lakukan reset ke setelan pabrik untuk menonaktifkan Mode Tes Otomatis."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Konsol serial diaktifkan"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performa terpengaruh. Untuk menonaktifkan, periksa bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE eksperimental diaktifkan"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> diaktifkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dinonaktifkan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lepaskan tombol volume. Untuk mengaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tekan dan tahan kedua tombol volume lagi selama 3 detik."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Pilih fitur yang akan digunakan saat mengetuk tombol aksesibilitas:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Pilih fitur yang akan digunakan dengan gestur aksesibilitas (geser ke atas dari bawah layar dengan dua jari):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Pilih fitur yang akan digunakan dengan gestur aksesibilitas (geser ke atas dari bawah layar dengan tiga jari):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Sentuh lama tombol aksesibilitas untuk beralih ke fitur lain."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Geser ke atas dengan dua jari dan tahan untuk beralih ke fitur lain."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Geser ke atas dengan tiga jari dan tahan untuk beralih ke fitur lain."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Pembesaran"</string>
<string name="user_switched" msgid="7249833311585228097">"Pengguna saat ini <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Beralih ke <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Panggilan sedang berlangsung"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Menyaring panggilan masuk"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Tidak dikategorikan"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promosi"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Sosial"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Berita"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Rekomendasi"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Anda menyetel nilai penting notifikasi ini."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ini penting karena orang-orang yang terlibat."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notifikasi aplikasi kustom"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Pemilih Pintasan Aksesibilitas di layar"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Pintasan Aksesibilitas"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Tutup Menu Notifikasi"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Putar/Jeda Media"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad Atas"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad Bawah"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Kiri"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 9495626..01e2f6f 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Veldu til að slökkva á þráðlausri villuleit."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Kveikt á stillingu prófunarvangs"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Núllstilltu til að slökkva á stillingu prófunarvangs."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Raðstjórnborð virkjað"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Þetta hefur áhrif á afköst. Athugaðu ræsiforritið ef þú vilt gera þetta óvirkt."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Kveikt á MTE á tilraunarstigi"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Slepptu hljóðstyrkstökkunum. Til að kveikja á <xliff:g id="SERVICE_NAME">%1$s</xliff:g> skaltu halda báðum hljóðstyrkstökkunum aftur inni í 3 sekúndur."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Veldu eiginleika sem á að nota þegar ýtt er á aðgengishnappinn:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Veldu eiginleika sem á að nota með aðgengisbendingunni (strjúka upp frá neðri hluta skjásins með tveimur fingrum):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Veldu eiginleika sem á að nota með aðgengisbendingunni (strjúka upp frá neðri hluta skjásins með þremur fingrum):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Til að skipta á milli eiginleika skaltu halda aðgengishnappinum inni."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Til að skipta á milli eiginleika skaltu strjúka upp með tveimur fingrum og halda inni."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Til að skipta á milli eiginleika skaltu strjúka upp með þremur fingrum og halda inni."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Stækkun"</string>
<string name="user_switched" msgid="7249833311585228097">"Núverandi notandi <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Skiptir yfir á <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Símtal í gangi"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Síar símtal sem berst"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Óflokkað"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Þú stilltir mikilvægi þessara tilkynninga."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Þetta er mikilvægt vegna fólksins sem tekur þátt í þessu."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Sérsniðin forritatilkynning"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Val um flýtileið í aðgengiseiginleika á skjánum"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Flýtileið aðgengisstillingar"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Loka tilkynningaglugga"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Upphnappur stýriflatar"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Niðurhnappur stýriflatar"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Vinstrihnappur stýriflatar"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 3952837..240f318 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -420,9 +420,9 @@
<string name="permdesc_runInBackground" msgid="4344539472115495141">"Questa app può essere eseguita in background, velocizzando il consumo della batteria."</string>
<string name="permlab_useDataInBackground" msgid="783415807623038947">"utilizzo dei dati in background"</string>
<string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Questa app può usare dati in background, aumentando l\'utilizzo dei dati."</string>
- <string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"Pianificare la precisione delle azioni temporizzate"</string>
+ <string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"Programma azioni temporizzate con precisione"</string>
<string name="permdesc_schedule_exact_alarm" msgid="8198009212013211497">"Questa app può pianificare il lavoro al momento desiderato nel futuro. Significa anche che l\'app può essere eseguita quando non stai attivamente utilizzando il dispositivo."</string>
- <string name="permlab_use_exact_alarm" msgid="348045139777131552">"Pianifica sveglie o promemoria eventi"</string>
+ <string name="permlab_use_exact_alarm" msgid="348045139777131552">"Programma sveglie o promemoria eventi"</string>
<string name="permdesc_use_exact_alarm" msgid="7033761461886938912">"Quest\'app può pianificare azioni come sveglie e promemoria per notificarti al momento desiderato nel futuro."</string>
<string name="permlab_persistentActivity" msgid="464970041740567970">"esecuzione permanente delle applicazioni"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Consente all\'applicazione di rendere persistenti in memoria alcune sue parti. Ciò può limitare la memoria disponibile per altre applicazioni, rallentando il tablet."</string>
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Seleziona per disattivare il debug wireless."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modalità test harness attivata"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Ripristina le impostazioni di fabbrica per disattivare la modalità test harness."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Console seriale attivata"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Ci sono conseguenze sulle prestazioni. Per disattivare, seleziona il bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Funzionalità MTE sperimentale attivata"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Rilascia i tasti del volume. Per attivare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tieni di nuovo premuti entrambi i tasti del volume per 3 secondi."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Scegli una funzionalità da usare quando tocchi il pulsante Accessibilità:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Scegli una funzionalità da usare con il gesto di accessibilità (scorrimento verso l\'alto dalla parte inferiore dello schermo con due dita):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Scegli una funzionalità da usare con il gesto di accessibilità (scorrimento verso l\'alto dalla parte inferiore dello schermo con tre dita):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Per spostarti tra le funzionalità, tocca e tieni premuto il pulsante Accessibilità."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Per spostarti tra le funzionalità, scorri verso l\'alto con due dita e tieni premuto."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Per spostarti tra le funzionalità, scorri verso l\'alto con tre dita e tieni premuto."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ingrandimento"</string>
<string name="user_switched" msgid="7249833311585228097">"Utente corrente <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Passaggio a <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chiamata in corso"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Applicazione filtro a chiamata in arrivo"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Senza categoria"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Stabilisci tu l\'importanza di queste notifiche."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Importante a causa delle persone coinvolte."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notifica app personalizzata"</string>
@@ -2193,6 +2211,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selettore scorciatoia Accessibilità sullo schermo"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Scorciatoia Accessibilità"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ignora area notifiche"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"D-pad - Su"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"D-pad - Giù"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad - Sinistra"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 9c143c7..9e68dd5 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"יש לבחור כדי להשבית ניפוי באגים אלחוטי."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"מצב מסגרת בדיקה הופעל"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"כדי להשבית את מצב \'מסגרת בדיקה\' צריך לאפס להגדרות היצרן."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"קונסולה סדרתית מופעלת"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"קיימת השפעה על הביצועים. כדי להשבית, יש לבדוק את תוכנת האתחול."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ה-MTE הניסיוני הופעל"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"אפשר לשחרר את מקש עוצמת הקול. כדי להפעיל את השירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, צריך ללחוץ לחיצה ארוכה על שני המקשים של עוצמת הקול שוב במשך 3 שניות."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"צריך לבחור תכונה שתופעל כשלוחצים על לחצן הנגישות:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"יש לבחור תכונה שתופעל באמצעות תנועת הנגישות (החלקה למעלה מתחתית המסך בעזרת שתי אצבעות):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"יש לבחור תכונה שתופעל באמצעות תנועת הנגישות (החלקה למעלה מתחתית המסך בעזרת שלוש אצבעות):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"כדי לעבור בין תכונות, יש ללחוץ לחיצה ארוכה על לחצן הנגישות."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"כדי לעבור בין תכונות, יש להחליק כלפי מעלה בעזרת שתי אצבעות ולהחזיק."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"כדי לעבור בין תכונות, יש להחליק כלפי מעלה בעזרת שלוש אצבעות ולהחזיק."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"הגדלה"</string>
<string name="user_switched" msgid="7249833311585228097">"המשתמש הנוכחי <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"מעבר אל <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"שיחה פעילה"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"סינון שיחה נכנסת"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"ללא שיוך לקטגוריה"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"עליך להגדיר את החשיבות של ההתראות האלה."</string>
<string name="importance_from_person" msgid="4235804979664465383">"ההודעה חשובה בשל האנשים המעורבים."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"התראות אפליקציה בהתאמה אישית"</string>
@@ -2193,6 +2211,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"בורר קיצורי דרך לנגישות במסך"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"קיצור דרך לנגישות"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"סגירת לוח ההתראות"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"לחצן עליון ב-Dpad"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"לחצן תחתון ב-Dpad"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"לחצן שמאלי ב-Dpad"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 934967f..db56a3a 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -157,7 +157,7 @@
<string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"暗号化、暗号化されていないネットワークに関する通知"</string>
<string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"デバイス ID へのアクセスが発生しました"</string>
<string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>、<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> の SIM の使用中に付近のネットワークでお使いのデバイスの一意の ID(IMSI または IMEI)が記録されました"</string>
- <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>、<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> の SIM の使用中に付近のネットワークでお使いのデバイスの一意の ID(IMSI または IMEI)が記録されました。\n\nつまり、あなたの位置情報、アクティビティ、身元などが記録されことになります。これはよくある事象ですが、プライバシーに不安を持たれている人にとっては問題になる可能性があります。"</string>
+ <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"<xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>、<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> の SIM の使用中に、お使いのデバイスの一意の ID(IMSI または IMEI)が付近のネットワークで記録されました。\n\nつまり、あなたの位置情報、アクティビティ、身元などが記録されたことになります。これはよくある事象ですが、プライバシーを保護したいユーザーにとっては問題となる可能性があります。"</string>
<string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"暗号化されたネットワーク(<xliff:g id="NETWORK_NAME">%1$s</xliff:g>)に接続しました"</string>
<string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> の SIM 接続のセキュリティが強化されました"</string>
<string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"接続先のネットワークが暗号化されていません"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ワイヤレス デバッグを無効にするには選択します。"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"テストハーネス モード有効"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"出荷時設定にリセットしてテストハーネス モードを無効にしてください。"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"シリアル コンソールは有効です"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"シリアル コンソールを有効にすると、パフォーマンスに影響します。無効にするには、ブートローダーをチェックしてください。"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"試験運用版 MTE を有効にしました"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"音量ボタンを離してください。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を有効にするには音量大と音量小の両方のボタンを 3 秒ほど長押ししてください。"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ユーザー補助機能ボタンをタップした場合に使用する機能を選択してください。"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ユーザー補助操作(2 本の指で画面の下から上にスワイプ)で使用する機能を選択してください。"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ユーザー補助操作(3 本の指で画面の下から上にスワイプ)で使用する機能を選択してください。"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"機能を切り替えるには、ユーザー補助機能ボタンを長押しします。"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"機能を切り替えるには、2 本の指で上にスワイプしたまま長押しします。"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"機能を切り替えるには、3 本の指で上にスワイプしたまま長押しします。"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"拡大"</string>
<string name="user_switched" msgid="7249833311585228097">"現在のユーザーは<xliff:g id="NAME">%1$s</xliff:g>です。"</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>に切り替えています…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"着信をスクリーニング中"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"カテゴリなし"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"プロモーション"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"ソーシャル"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ニュース"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"推奨事項"</string>
<string name="importance_from_user" msgid="2782756722448800447">"このような通知の重要度を設定します。"</string>
<string name="importance_from_person" msgid="4235804979664465383">"関係するユーザーのため、この設定は重要です。"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"カスタムアプリ通知"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"画面上のユーザー補助機能のショートカットの選択メニュー"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ユーザー補助のショートカット"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"通知シェードを閉じる"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"メニュー"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"メディアの再生 / 一時停止"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"D-pad: 上"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"D-pad: 下"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad: 左"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 013125e..6bfacd4 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"აირჩიეთ შეცდომების უსადენო გამართვის გასათიშად."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"რეჟიმი „გარემო ტესტირებისთვის“ ჩართულია"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"დააბრუნეთ ქარხნული პარამეტრები „გარემო ტესტირებისთვის“ რეჟიმის გასათიშად."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"სერიული კონსოლი ჩართულია"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"მუშაობა შეფერხებულია. გასათიშად მონიშნეთ ჩამტვირთავი."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ექსპერიმენტული MTE ჩართულია"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ჩართულია."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> გამორთულია."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ხელი აუშვით ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-ის ჩასართველად, ხელმეორედ ხანგრძლივად დააჭირეთ ორივე ხმის ღილაკს 3 წამის განმავლობაში."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"აირჩიეთ მარტივი წვდომის ღილაკზე შეხებისას გამოსაყენებელი ფუნქცია:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"აირჩიეთ მარტივი წვდომის ჟესტთან (ორი თითით გადაფურცვლა ეკრანის ქვედა კიდიდან ზემოთ) გამოსაყენებელი ფუნქცია:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"აირჩიეთ მარტივი წვდომის ჟესტთან (სამი თითით გადაფურცვლა ეკრანის ქვედა კიდიდან ზემოთ) გამოსაყენებელი ფუნქცია:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"სერვისების გადასართავად, ხანგრძლივად შეეხეთ მარტივი წვდომის ღილაკს."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ფუნქციების გადასართავად, ორი თითით გადაფურცლეთ ზემოთ და დააყოვნეთ."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ფუნქციების გადასართავად, სამი თითით გადაფურცლეთ ზემოთ და დააყოვნეთ."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"გადიდება"</string>
<string name="user_switched" msgid="7249833311585228097">"ამჟამინდელი მომხმარებელი <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>-ზე გადართვა…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"მიმდინარე ზარი"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"შემომავალი ზარების გაცხრილვა"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"კატეგორიის გარეშე"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"სარეკლამო აქციები"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"სოციალური ქსელები"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ახალი ამბები"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"რეკომენდაციები"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ამ შეტყობინებების მნიშვნელობის დონე განისაზღვრა თქვენ მიერ."</string>
<string name="importance_from_person" msgid="4235804979664465383">"მნიშვნელოვანია ჩართული მომხმარებლების გამო."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"აპის მორგებული შეტყობინება"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"მისაწვდომობის ეკრანული მალსახმობის ამომრჩევი"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"მისაწვდომობის მალსახმობი"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"შეტყობინებების ფარდის დახურვა"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"მენიუ"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"მედიის დაკვრა/დაპაუზება"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ზევით"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ქვევით"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad მარცხნივ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 791644d..16e86325 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Сымсыз түзетуді өшіру үшін басыңыз."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Сынақ бағдарламасы режимі қосылды"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Сынақ бағдарламасы режимін өшіру үшін зауыттық күйіне қайтарыңыз."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Сериялық консоль қосылды"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Өнімділікке әсер етеді. Өшіру үшін жүктегішті тексеріңіз."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Эксперименттік MTE қосылды"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Дыбыс деңгейі пернелерін жіберіңіз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін қосу үшін дыбыс деңгейі пернесінің екеуін де қайтадан 3 секундқа басып тұрыңыз."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"\"Арнайы мүмкіндіктер\" түймесін түрткенде пайдаланатын функцияны таңдаңыз:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Арнайы мүмкіндіктер қимылымен (екі саусақпен экранның төменгі жағынан жоғары қарай сырғытыңыз) пайдаланатын функцияны таңдаңыз:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Арнайы мүмкіндіктер қимылымен (үш саусақпен экранның төменгі жағынан жоғары қарай сырғытыңыз) пайдаланатын функцияны таңдаңыз:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Бір функциядан екінші функцияға ауысу үшін \"Арнайы мүмкіндіктер\" түймесін түртіп, оны ұстап тұрыңыз."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Бір функциядан екінші функцияға ауысу үшін екі саусақпен жоғары қарай сырғытып, ұстап тұрыңыз."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Бір функциядан екінші функцияға ауысу үшін үш саусақпен жоғары қарай сырғытып, ұстап тұрыңыз."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ұлғайту"</string>
<string name="user_switched" msgid="7249833311585228097">"Ағымдағы пайдаланушы <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> профиліне ауысу…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Қоңырау"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Келген қоңырауды сүзу"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Санатқа жатқызылмаған"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Сіз осы хабарландырулардың маңыздылығын орнатасыз."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Қатысты адамдарға байланысты бұл маңызды."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Арнаулы қолданба хабарландыруы"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Экрандағы арнайы мүмкіндіктерді жылдам қосу әрекетін таңдау"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Арнайы мүмкіндіктерді жылдам қосу"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Хабарландыру тақтасын жабу"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Жоғарғы Dpad түймесі"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Төменгі Dpad түймесі"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Сол жақ Dpad түймесі"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 12e1a36..5a5a8da2 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ជ្រើសរើស ដើម្បីបិទការជួសជុលដោយឥតខ្សែ។"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"បានបើកមុខងារប្រមូលធ្វើតេស្ត"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"ធ្វើការកំណត់ដូចដើមឡើងវិញ ដើម្បីបិទមុខងារប្រមូលធ្វើតេស្ត។"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"កុងសូលស៊េរីត្រូវបានបើក"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"ប្រតិបត្តិការទទួលរងការប៉ះពាល់។ សូមពិនិត្យមើលកម្មវិធីដំណើរការប្រព័ន្ធ ដើម្បីបិទ។"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"បានបើក MTE ពិសោធន៍"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"លែងគ្រាប់ចុចកម្រិតសំឡេង។ ដើម្បីបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g> សូមចុចគ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់ម្ដងទៀតរយៈពេល 3 វិនាទី។"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ជ្រើសរើសមុខងារដែលត្រូវប្រើ នៅពេលដែលអ្នកចុចប៊ូតុងភាពងាយស្រួល៖"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ជ្រើសរើសមុខងារ ដើម្បីប្រើជាមួយចលនាភាពងាយស្រួល (អូសឡើងលើពីផ្នែកខាងក្រោមនៃអេក្រង់ដោយប្រើម្រាមដៃពីរ)៖"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ជ្រើសរើសមុខងារ ដើម្បីប្រើជាមួយចលនាភាពងាយស្រួល (អូសឡើងលើពីផ្នែកខាងក្រោមនៃអេក្រង់ដោយប្រើម្រាមដៃបី)៖"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ដើម្បីប្ដូររវាងមុខងារផ្សេងៗ សូមចុចប៊ូតុងភាពងាយស្រួលឱ្យជាប់។"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ដើម្បីប្ដូររវាងមុខងារផ្សេងៗ សូមអូសឡើងលើដោយប្រើម្រាមដៃពីរ ហើយសង្កត់ឱ្យជាប់។"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ដើម្បីប្ដូររវាងមុខងារផ្សេងៗ សូមអូសឡើងលើដោយប្រើម្រាមដៃបី ហើយសង្កត់ឱ្យជាប់។"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ការពង្រីក"</string>
<string name="user_switched" msgid="7249833311585228097">"អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="NAME">%1$s</xliff:g> ។"</string>
<string name="user_switching_message" msgid="1912993630661332336">"កំពុងប្ដូរទៅ <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"ការហៅដែលកំពុងដំណើរការ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"កំពុងពិនិត្យការហៅចូល"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"មិនបែងចែកប្រភេទ"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"ប្រូម៉ូសិន"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"សង្គម"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ព័ត៌មាន"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"ការណែនាំ"</string>
<string name="importance_from_user" msgid="2782756722448800447">"អ្នកបានកំណត់សារៈសំខាន់នៃការជូនដំណឹងទាំងនេះ"</string>
<string name="importance_from_person" msgid="4235804979664465383">"វាមានសារៈសំខាន់ដោយសារតែមនុស្សដែលពាក់ព័ន្ធ"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ការជូនដំណឹងកម្មវិធីផ្ទាល់ខ្លួន"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ម៉ឺនុយជ្រើសរើសផ្លូវកាត់ភាពងាយស្រួលនៅលើអេក្រង់"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ផ្លូវកាត់ភាពងាយស្រួល"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"ច្រានចោលផ្ទាំងជូនដំណឹង"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"ម៉ឺនុយ"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"ចាក់/ផ្អាកមេឌៀ"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ឡើងលើ"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ចុះក្រោម"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ឆ្វេង"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index c134b74..9131b5a 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ವೈರ್ಲೆಸ್ ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಆಯ್ಕೆ ಮಾಡಿ."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವಿಕೆ ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವಿಕೆ ಮೋಡ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್ ಮಾಡಬೇಕು."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"ಸರಣಿ ಕನ್ಸೋಲ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"ಕಾರ್ಯಕ್ಷಮತೆಯ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುತ್ತದೆ. ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು, ಬೂಟ್ಲೋಡರ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ಪ್ರಾಯೋಗಿಕ MTE ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಬಿಡುಗಡೆ ಮಾಡಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲು, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಮತ್ತೊಮ್ಮೆ 3 ಸೆಕೆಂಡ್ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ನೀವು ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಬಟನ್ ಟ್ಯಾಪ್ ಮಾಡಿದಾಗ ಬಳಸುವುದಕ್ಕಾಗಿ ವೈಶಿಷ್ಟ್ಯವೊಂದನ್ನು ಆರಿಸಿ:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಗೆಸ್ಚರ್ನೊಂದಿಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯವೊಂದನ್ನು ಆಯ್ಕೆಮಾಡಿ (ಎರಡು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಪರದೆಯ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಗೆಸ್ಚರ್ನೊಂದಿಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯವೊಂದನ್ನು ಆಯ್ಕೆಮಾಡಿ (ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಪರದೆಯ ಕೆಳಭಾಗದಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ವೈಶಿಷ್ಟ್ಯಗಳ ನಡುವೆ ಬದಲಿಸಲು, ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಬಟನ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ವೈಶಿಷ್ಟ್ಯಗಳ ನಡುವೆ ಬದಲಿಸಲು, ಎರಡು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಒತ್ತಿ ಹಿಡಿಯಿರಿ."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ವೈಶಿಷ್ಟ್ಯಗಳ ನಡುವೆ ಬದಲಿಸಲು, ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಒತ್ತಿ ಹಿಡಿಯಿರಿ."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ಹಿಗ್ಗಿಸುವಿಕೆ"</string>
<string name="user_switched" msgid="7249833311585228097">"ಪ್ರಸ್ತುತ ಬಳಕೆದಾರರು <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>ಗೆ ಬದಲಾಯಿಸಲಾಗುತ್ತಿದೆ…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ಒಳಬರುವ ಕರೆಯನ್ನು ಸ್ಕ್ರೀನ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"ವರ್ಗೀಕರಿಸದಿರುವುದು"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"ಪ್ರೊಮೋಷನ್ಗಳು"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"ಸಾಮಾಜಿಕ"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ಸುದ್ದಿ"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"ಶಿಫಾರಸುಗಳು"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ನೀವು ಈ ಅಧಿಸೂಚನೆಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಿರುವಿರಿ."</string>
<string name="importance_from_person" msgid="4235804979664465383">"ಜನರು ತೊಡಗಿಕೊಂಡಿರುವ ಕಾರಣ ಇದು ಅತ್ಯಂತ ಪ್ರಮುಖವಾಗಿದೆ."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ಕಸ್ಟಮ್ ಆ್ಯಪ್ ನೋಟಿಫಿಕೇಶನ್"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ಸ್ಕ್ರೀನ್ನಲ್ಲಿನ ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಶಾರ್ಟ್ಕಟ್ ಆಯ್ಕೆ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಶಾರ್ಟ್ಕಟ್"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"ಅಧಿಸೂಚನೆಯ ಪರದೆಯನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"ಮೆನು"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"ಮೀಡಿಯಾವನ್ನು ಪ್ಲೇ ಮಾಡಿ/ವಿರಾಮಗೊಳಿಸಿ"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ನ ಮೇಲಿನ ಬಟನ್"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ನ ಕೆಳಗಿನ ಬಟನ್"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ನ ಎಡಭಾಗದ ಬಟನ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 62ec778..804957c 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"무선 디버깅을 사용 중지하려면 선택하세요."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"테스트 하네스 모드 사용 설정됨"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"테스트 하네스 모드를 사용 중지하려면 초기화하세요."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"시리얼 콘솔 사용 설정됨"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"성능에 영향을 미쳤습니다. 사용 중지하려면 부트로더를 확인하세요."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"실험용 MTE 사용 설정됨"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"볼륨 키에서 손을 뗍니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>을 켜려면 볼륨 키 2개를 3초 동안 길게 누르세요."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"접근성 버튼을 탭할 때 사용할 기능을 선택하세요."</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"접근성 동작(두 손가락을 사용하여 화면 하단에서 위로 스와이프)으로 실행할 기능을 선택하세요."</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"접근성 동작(세 손가락을 사용하여 화면 하단에서 위로 스와이프)으로 실행할 기능을 선택하세요."</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"기능 간에 전환하려면 접근성 버튼을 길게 터치합니다."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"기능 간에 전환하려면 두 손가락을 사용하여 위로 스와이프한 다음 잠시 기다립니다."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"기능 간에 전환하려면 세 손가락을 사용하여 위로 스와이프한 다음 잠시 기다립니다."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"확대"</string>
<string name="user_switched" msgid="7249833311585228097">"현재 사용자는 <xliff:g id="NAME">%1$s</xliff:g>님입니다."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>로 전환하는 중…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"진행 중인 통화"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"수신 전화 검사 중"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"지정된 카테고리 없음"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"이러한 알림의 중요도를 설정했습니다."</string>
<string name="importance_from_person" msgid="4235804979664465383">"관련된 사용자가 있으므로 중요합니다."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"맞춤 앱 알림"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"화면상의 접근성 바로가기 선택 도구"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"접근성 단축키"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"알림 창 닫기"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"방향 패드 위쪽"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"방향 패드 아래"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"방향 패드 왼쪽"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 4df1e7e..da709f7 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоону өчүрүңүз."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Сыноо программасынын режими иштетилди"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Сыноо программасынын режимин өчүрүү үчүн баштапкы параметрлерге кайтарыңыз."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Сериялык консоль иштетилди"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Майнаптуулугуна таасири тиет. Өчүрүү үчүн операциялык тутумду жүктөгүчтү текшериңиз."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Cынамык MTE иштетилди"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Үн баскычтарын коё бериңиз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын күйгүзүү үчүн үн баскычтарын кайра 3 секунд коё бербей басып туруңуз."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Атайын мүмкүнчүлүктөр баскычын таптаганыңызда иштей турган функцияны тандаңыз:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Атайын мүмкүнчүлүктөр жаңсоосу үчүн функцияны тандаңыз (эки манжаңыз менен экранды ылдыйдан өйдө сүрүңүз):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Атайын мүмкүнчүлүктөр жаңсоосу аркылуу иштетиле турган функцияны тандаңыз (үч манжаңыз менен экранды ылдыйдан өйдө сүрүңүз):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Функцияларды которуштуруу үчүн, Атайын мүмкүнчүлүктөр баскычын басып, кармап туруңуз."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Функцияларды которуштуруу үчүн, эки манжаңыз менен өйдө сүрүп, кармап туруңуз."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Башка функцияга которулуу үчүн үч манжаңыз менен экранды өйдө сүрүп, кармап туруңуз."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Чоңойтуу"</string>
<string name="user_switched" msgid="7249833311585228097">"Учурдагы колдонуучу <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> дегенге которулууда…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Учурдагы чалуу"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Кирүүчү чалууну иргөө"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Категорияларга бөлүнгөн эмес"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Бул эскертмелердин маанилүүлүгүн белгиледиңиз."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Булар сиз үчүн маанилүү адамдар."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Колдонмонун ыңгайлаштырылган билдирмеси"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ыкчам иштетүү менюсу"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Ыкчам иштетүү"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Билдирмелер тактасын жабуу"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad\'дын жогорку баскычы"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad\'дын ылдыйкы баскычы"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad\'дын сол баскычы"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 3b77acb..0ee3d50 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ເລືອກເພື່ອປິດການນຳໃຊ້ການດີບັກໄຮ້ສາຍ."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"ເປີດໃຊ້ໂໝດ Test Harness ແລ້ວ"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"ດຳເນີນການຣີເຊັດເປັນຄ່າຈາກໂຮງງານເພື່ອປິດການນຳໃຊ້ໂໝດ Test Harness."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"ເປີດນຳໃຊ້ຊີຣຽວຄອນໂຊແລ້ວ"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"ມີຜົນກະທົບກັບປະສິດທິພາບ. ເພື່ອປິດການນຳໃຊ້, ໃຫ້ກວດສອບ bootloader ເບິ່ງ."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ເປີດການນຳໃຊ້ MTE ແບບທົດລອງແລ້ວ"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ເປີດໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ແລ້ວ."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ປິດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ໄວ້ແລ້ວ."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ປ່ອຍປຸ່ມລະດັບສຽງ. ເພື່ອເປີດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ໃຫ້ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີ."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ເລືອກຄຸນສົມບັດທີ່ຈະໃຊ້ເມື່ອທ່ານແຕະປຸ່ມການຊ່ວຍເຂົ້າເຖິງ:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບທ່າທາງການຊ່ວຍເຂົ້າເຖິງ (ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍດ້ວຍສອງນິ້ວ):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບທ່າທາງການຊ່ວຍເຂົ້າເຖິງ (ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍດ້ວຍສາມນິ້ວ):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ເພື່ອສະຫຼັບລະຫວ່າງຄຸນສົມບັດຕ່າງໆ, ໃຫ້ແຕະໃສ່ປຸ່ມການຊ່ວຍເຂົ້າເຖິງຄ້າງໄວ້."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ເພື່ອສະຫຼັບລະຫວ່າງບໍລິຄຸນສົມບັດຕ່າງໆ, ໃຫ້ປັດຂຶ້ນດ້ວຍສອງນິ້ວຄ້າງໄວ້."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ເພື່ອສະຫຼັບລະຫວ່າງຄຸນສົມບັດຕ່າງໆ, ໃຫ້ປັດຂຶ້ນດ້ວຍສາມນິ້ວຄ້າງໄວ້."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ການຂະຫຍາຍ"</string>
<string name="user_switched" msgid="7249833311585228097">"ຜູ່ໃຊ້ປັດຈຸບັນ <xliff:g id="NAME">%1$s</xliff:g> ."</string>
<string name="user_switching_message" msgid="1912993630661332336">"ກຳລັງສະຫຼັບໄປຫາ<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"ສາຍໂທອອກ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ກຳລັງກວດສອບສາຍໂທເຂົ້າ"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"ບໍ່ມີໝວດໝູ່"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"ໂປຣໂມຊັນ"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"ສັງຄົມ"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ຂ່າວ"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"ການແນະນຳ"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ທ່ານຕັ້ງຄວາມສຳຄັນຂອງການແຈ້ງເຕືອນເຫຼົ່ານີ້."</string>
<string name="importance_from_person" msgid="4235804979664465383">"ຂໍ້ຄວາມນີ້ສຳຄັນເນື່ອງຈາກບຸກຄົນທີ່ກ່ຽວຂ້ອງ."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ການແຈ້ງເຕືອນແອັບແບບກຳນົດເອງ"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ຕົວເລືອກທາງລັດການຊ່ວຍເຂົ້າເຖິງຢູ່ໜ້າຈໍ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ທາງລັດການຊ່ວຍເຂົ້າເຖິງ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"ປິດເງົາການແຈ້ງເຕືອນໄວ້"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"ເມນູ"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"ຫຼິ້ນ/ຢຸດສື່ຊົ່ວຄາວ"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ຂຶ້ນ"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ລົງ"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ຊ້າຍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index b864773..43a619d 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Pasirinkite, kad išjungtumėte belaidžio ryšio derinimą."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Testavimo sistemos režimas įgalintas"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Atkurkite gamyklinius duomenis, kad išjungtumėte testavimo sistemos režimą."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serijos pultas įgalintas"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Našumas paveiktas. Norėdami išjungti, patikrinkite paleidyklę."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentiniai atminties žymėjimo plėtiniai (angl. „Memory Tagging Extensions“, MTE) įgalinti"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Atleiskite garsumo klavišus. Kad įjungtumėte „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite ir 3 sekundes palaikykite garsumo klavišus."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Pasirinkite funkciją, kuri bus naudojama, kai paliesite pritaikomumo mygtuką:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Pasirinkite funkciją, kuri bus naudojama su pritaikomumo gestu (perbraukimas aukštyn dviem pirštais iš ekrano apačios):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Pasirinkite funkciją, kuri bus naudojama su pritaikomumo gestu (perbraukimas aukštyn trimis pirštais iš ekrano apačios):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Norėdami perjungti funkcijas, palieskite ir palaikykite pritaikomumo mygtuką."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Norėdami perjungti funkcijas, perbraukite aukštyn dviem pirštais ir palaikykite."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Norėdami perjungti funkcijas, perbraukite aukštyn trimis pirštais ir palaikykite."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Didinimas"</string>
<string name="user_switched" msgid="7249833311585228097">"Dabartinis naudotojas: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Perjungiama į <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1978,6 +1988,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Vykstantis skambutis"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Gaunamojo skambučio tikrinimas"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Be kategorijos"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Akcijos"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Socialiniai tinklai"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Naujienos"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Rekomendacijos"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Galite nustatyti šių pranešimų svarbą."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Tai svarbu dėl susijusių žmonių."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Tinkintas programos pranešimas"</string>
@@ -2194,6 +2208,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekrano pritaikomumo šaukinių parinkiklis"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Pritaikomumo šaukinys"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Atsisakyti pranešimų skydelio"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Meniu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Leisti / pristabdyti mediją"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Valdymo pultas – aukštyn"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Valdymo pultas – žemyn"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Valdymo pultas – kairėn"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 9147e9b..8481d988 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Atlasiet, lai atspējotu bezvadu atkļūdošanu."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Drošības pārbaudes režīms ir iespējots"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Lai atspējotu drošības pārbaudes režīmu, veiciet rūpnīcas datu atiestatīšanu."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Seriālā konsole ir iespējota"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Tiek ietekmēta veiktspēja. Lai atspējotu, pārbaudiet operētājsistēmu ielādes rīku."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Eksperimentālais paplašinājums MTE iespējots"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Atlaidiet skaļuma pogas. Lai ieslēgtu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, vēlreiz nospiediet un trīs sekundes turiet nospiestas abas skaļuma pogas."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Izvēlieties funkciju, ko izmantot, kad pieskaraties pieejamības pogai."</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Izvēlieties funkciju, ko izmantot ar pieejamības žestu (vilkšana ar diviem pirkstiem augšup no ekrāna apakšdaļas)."</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Izvēlieties funkciju, ko izmantot ar pieejamības žestu (vilkšana ar trīs pirkstiem augšup no ekrāna apakšdaļas)."</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Lai pārslēgtu funkcijas, pieskarieties pieejamības pogai un turiet to."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Lai pārslēgtu funkcijas, velciet ar diviem pirkstiem augšup un turiet."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Lai pārslēgtu funkcijas, velciet ar trīs pirkstiem augšup un turiet."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Palielinājums"</string>
<string name="user_switched" msgid="7249833311585228097">"Pašreizējais lietotājs: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Notiek pāriešana uz: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pašreizējais zvans"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Ienākošā zvana filtrēšana"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Nav kategorijas"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Akcijas"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Sociālie tīkli"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Ziņas"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Ieteikumi"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Jūs iestatījāt šo paziņojumu svarīguma līmeni."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Tas ir svarīgi iesaistīto personu dēļ."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Pielāgots lietotnes paziņojums"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekrāna pieejamības saīsnes atlasītājs"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Pieejamības saīsne"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Nerādīt paziņojumu paneli"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Izvēlne"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Sākt/apturēt multivides atskaņošanu"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Virzienu slēdzis — augšup"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Virzienu slēdzis — lejup"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Virzienu slēdzis — pa kreisi"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index f4ed853..1311690 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -209,7 +209,7 @@
<string name="location_changed_notification_text" msgid="7158423339982706912">"Контактирајте со IT-администраторот за да дознаете повеќе"</string>
<string name="geofencing_service" msgid="3826902410740315456">"Услуга за виртуелна географска граница"</string>
<string name="country_detector" msgid="7023275114706088854">"Детектор на земја"</string>
- <string name="location_service" msgid="2439187616018455546">"Услуга за локација"</string>
+ <string name="location_service" msgid="2439187616018455546">"Локациска услуга"</string>
<string name="gnss_service" msgid="8907781262179951385">"Услуга GNSS"</string>
<string name="sensor_notification_service" msgid="7474531979178682676">"Услуга за известување од сензорот"</string>
<string name="twilight_service" msgid="8964898045693187224">"Услуга за самрак"</string>
@@ -498,9 +498,9 @@
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"пристапи кон наредби на давателот на дополнителна локација"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Овозможува апликацијата да пристапи кон дополнителни наредби на давател на локација. Ова може да овозможи апликацијата да го попечи функционирањето на GPS или други извори на локација."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"пристап до прецизната локација само во преден план"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Апликацијава може да ја добие вашата прецизна локација од „Услугите според локација“ кога се користи. „Услугите според локација“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата. Ова може да го зголеми користењето на батеријата."</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Апликацијава може да ја добие вашата прецизна локација од „Локациските услуги“ кога се користи. „Локациските услуги“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата. Ова може да го зголеми користењето на батеријата."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"пристап до приближната локација само во преден план"</string>
- <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Апликацијава може да ја добие вашата приближна локација од „Услугите според локација“ кога се користи. „Услугите според локација“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата."</string>
+ <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Апликацијава може да ја добие вашата приближна локација од „Локациските услуги“ кога се користи. „Локациските услуги“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата."</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"пристап до локацијата во заднина"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Апликацијава може да пристапува до локацијата во секое време, дури и кога не се користи."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"менува аудио поставки"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Изберете за да се оневозможи безжично отстранување грешки."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Овозможен е режимот на рамка за тестирање"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Извршете фабричко ресетирање за да го оневозможите режимот на рамка за тестирање."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Сериската конзола е овозможена"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Перформансите се засегнати. За да оневозможите, проверете го подигнувачот."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Овозможена е експериментална MTE"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Ослободете ги копчињата за јачина на звукот. Притиснете ги и задржете ги двете копчиња за јачина на звукот во траење од 3 секунди за да вклучите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Изберете функција за користење кога ќе го допрете копчето за пристапност:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Изберете ја функцијата што ќе ја користите со движењето за пристапност (повлекување нагоре од дното на екранот со два прста):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Изберете ја функцијата што ќе ја користите со движењето за пристапност (повлекување нагоре од дното на екранот со три прста):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"За префрлање помеѓу функциите, допрете и задржете го копчето за пристапност."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"За да се префрлите на друга функција, повлечете нагоре со два прста и задржете."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"За префрлање помеѓу функциите, повлечете нагоре со три прста и задржете."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Зголемување"</string>
<string name="user_switched" msgid="7249833311585228097">"Тековен корисник <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Се префрла на <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Тековен повик"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Проверка на дојдовен повик"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Некатегоризирано"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Ја поставивте важноста на известувањава."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ова е важно заради луѓето кои се вклучени."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Приспособено известување за апликација"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Избирач на кратенка за пристапност на екранот"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Кратенка за пристапност"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Отфрлете го панелот за известување"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Навигациско копче за нагоре"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Навигациско копче за надолу"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Навигациско копче за налево"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ede15a1..c856ec8 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"വയർലെസ് ഡീബഗ്ഗിംഗ് പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"പരിശോധനാ സംവിധാനങ്ങൾ മോഡ് പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"പരിശോധനാ സംവിധാന മോഡ് പ്രവർത്തനരഹിതമാക്കാൻ ഫാക്ടറി പുനഃക്രമീകരണം നിർവഹിക്കുക."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"സീരിയൽ കൺസോൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"പ്രകടനത്തെ ബാധിച്ചു. പ്രവർത്തനരഹിതമാക്കാൻ, ബൂട്ട് ലോഡർ പരിശോധിക്കുക."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"പരീക്ഷണാത്മക MTE പ്രവർത്തനക്ഷമമാക്കി"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"വോളിയം കീകൾ വിടുക. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കാൻ, രണ്ട് വോളിയം കീകളും വീണ്ടും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"നിങ്ങൾ ഉപയോഗസഹായി ബട്ടൺ ടാപ്പ് ചെയ്യുമ്പോൾ ഉപയോഗിക്കുന്നതിന് ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ഉപയോഗസഹായി വിരൽചലനത്തോടൊപ്പം ഉപയോഗിക്കാൻ ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക (രണ്ട് വിരലുകളുപയോഗിച്ച് സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ഉപയോഗസഹായി വിരൽചലനത്തോടൊപ്പം ഉപയോഗിക്കാൻ ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക (മൂന്ന് വിരലുകളുപയോഗിച്ച് സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ഫീച്ചറുകൾക്കിടയിൽ മാറാൻ, ഉപയോഗസഹായി ബട്ടൺ സ്പർശിച്ച് പിടിക്കുക."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ഫീച്ചറുകൾക്കിടയിൽ മാറാൻ, രണ്ട് വിരലുകളുപയോഗിച്ച് മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്ത് പിടിക്കുക."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ഫീച്ചറുകൾക്കിടയിൽ മാറാൻ, മൂന്ന് വിരലുകളുപയോഗിച്ച് മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്ത് പിടിക്കുക."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"മാഗ്നിഫിക്കേഷൻ"</string>
<string name="user_switched" msgid="7249833311585228097">"നിലവിലെ ഉപയോക്താവ് <xliff:g id="NAME">%1$s</xliff:g> ആണ്."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> എന്ന ഉപയോക്താവിലേക്ക് മാറുന്നു…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"സജീവമായ കോൾ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ഇൻകമിംഗ് കോൾ സ്ക്രീൻ ചെയ്യുന്നു"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"വർഗ്ഗീകരിച്ചിട്ടില്ലാത്ത"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"പ്രമോഷനുകൾ"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"സാമൂഹികം"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"വാർത്തകൾ"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"നിർദ്ദേശങ്ങൾ"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ഈ അറിയിപ്പുകളുടെ പ്രാധാന്യം നിങ്ങൾ സജ്ജീകരിച്ചു."</string>
<string name="importance_from_person" msgid="4235804979664465383">"ഉൾപ്പെട്ടിട്ടുള്ള ആളുകളെ കണക്കിലെടുക്കുമ്പോള് ഇത് പ്രധാനപ്പെട്ടതാണ്."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ഇഷ്ടാനുസൃത ആപ്പ് അറിയിപ്പുകൾ"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"സ്ക്രീനിലെ ഉപയോഗസഹായി കുറുക്കുവഴി ചൂസർ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ഉപയോഗസഹായി കുറുക്കുവഴി"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"അറിയിപ്പ് ഷെയ്ഡ് ഡിസ്മിസ് ചെയ്യുക"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"മെനു"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"മീഡിയ പ്ലേ ചെയ്യുക/താൽക്കാലികമായി നിർത്തുക"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad അപ്പ്"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ഡൗൺ"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ലെഫ്റ്റ്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 955b025..1dbbe99 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Wireless debugging-г идэвхгүй болгохын тулд сонгоно уу."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Туршилтын цогц горимыг идэвхжүүлсэн"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Туршилтын цогц горимыг идэвхгүй болгохын тулд үйлдвэрийн төлөвт шинэчилнэ үү."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Цуваа консолыг идэвхжүүлсэн"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Гүйцэтгэлд нөлөөлнө. Идэвхгүй болгохын тулд эхэлж ачаалагчийг шалгана уу."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Туршилтын MTE-г идэвхжүүлсэн"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Дууны түвшний товчнуудыг суллана уу. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаахын тулд дууны түвшний 2 товчийг зэрэг 3 секундийн турш удаан дарна уу."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Хандалтын товчлуурыг товшихдоо ашиглах онцлогийг сонгоно уу:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Хандалтын зангаагаар ашиглах үйлчилгээг сонгоно уу (хоёр хуруугаараа дэлгэцийн доороос дээш хоёр хуруугаар шударна уу):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Хандалтын зангаагаар ашиглах онцлогийг сонгоно уу (гурван хуруугаар дэлгэцийн доороос дээш шударна уу):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Онцлогуудын хооронд сэлгэхийн тулд хандалтын товчлуурыг удаан дарна уу."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Онцлогуудын хооронд сэлгэхийн тулд хоёр хуруугаар дээш шудраад удаан дарна уу."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Онцлогуудын хооронд сэлгэхийн тулд гурван хуруугаар дээш шудраад удаан дарна уу."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Томруулах"</string>
<string name="user_switched" msgid="7249833311585228097">"Одоогийн хэрэглэгч <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> руу сэлгэж байна…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Дуудлага хийгдэж байна"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Ирсэн дуудлагыг харуулж байна"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Ангилаагүй"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Та эдгээр мэдэгдлийн ач холбогдлыг тогтоосон."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Оролцсон хүмүүсээс шалтгаалан энэ нь өндөр ач холбогдолтой."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Аппын захиалгат мэдэгдэл"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Дэлгэц дээрх хандалтын товчлол сонгогч"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Хандалтын товчлол"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Мэдэгдлийн хураангуй самбарыг хаах"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad дээш"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad доош"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad зүүн"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 2df1bc9..3525c62 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"वायरलेस डीबगिंग बंद करण्यासाठी निवडा."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"टेस्ट हार्नेस मोड सुरू केला आहे"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"टेस्ट हार्नेस मोड बंद करण्यासाठी फॅक्टरी रीसेट करा."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"सिरीअल कन्सोल सुरू केला आहे"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"परफॉर्मन्सवर परिणाम होतो. बंद करण्यासाठी, बूटलोडर तपासा."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"प्रायोगिक MTE सुरू केले आहे"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"व्हॉल्यूम की रिलीझ करा. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू करण्यासाठी, दोन्ही व्हॉल्यूम की पुन्हा प्रेस करा आणि तीन सेकंदांसाठी धरून ठेवा."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"तुम्ही अॅक्सेसिबिलिटी बटणावर टॅप केल्यास वापरण्यासाठी एक वैशिष्ट्य निवडा:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"अॅक्सेसिबिलिटी जेश्चरसोबत वापराचे असलेले वैशिष्ट्य निवडा (दोन बोटांनी स्क्रीनच्या तळापासून वर स्वाइप करा):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"अॅक्सेसिबिलिटी जेश्चरसोबत वापराचे असलेले वैशिष्ट्य निवडा (तीनन बोटांनी स्क्रीनच्या तळापासून वर स्वाइप करा):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"वैशिष्टयांदरम्यान स्विच करण्यासाठी अॅक्सेसिबिलिटी बटणाला स्पर्श करा आणि धरून ठेवा."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"वैशिष्टयांदरम्यान स्विच करण्यासाठी दोन बोटांनी वर स्वाइप करा आणि धरून ठेवा."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"वैशिष्टयांदरम्यान स्विच करण्यासाठी तीन बोटांनी वर स्वाइप करा आणि धरून ठेवा."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"मॅग्निफिकेशन"</string>
<string name="user_switched" msgid="7249833311585228097">"वर्तमान वापरकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> वर स्विच करत आहे…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"सुरू असलेला कॉल"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"इनकमिंग कॉल स्क्रीन करत आहे"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"वर्गीकरण न केलेले"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"प्रमोशन"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"सोशल"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"बातम्या"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"शिफारशी"</string>
<string name="importance_from_user" msgid="2782756722448800447">"तुम्ही या सूचनांचे महत्त्व सेट केले."</string>
<string name="importance_from_person" msgid="4235804979664465383">"सामील असलेल्या लोकांमुळे हे महत्वाचे आहे."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"कस्टम ॲप सूचना"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ऑन-स्क्रीन ॲक्सेसिबिलिटी शॉर्टकट निवडकर्ता"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"अॅक्सेसिबिलिटी शॉर्टकट"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"सूचना शेड डिसमिस करा"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"मेनू"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"मीडिया प्ले करा/थांबवा"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad चे वरील"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad चे खालचे"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad डावीकडील"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 31180be..d03babb 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Pilih untuk melumpuhkan penyahpepijatan wayarles."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Mod Abah-abah Ujian didayakan"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Laksanakan tetapan semula kilang untuk melumpuhkan Mod Abah-abah Ujian."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Konsol bersiri didayakan"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Prestasi terjejas. Untuk melumpuhkan, semak pemuat but."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Percubaan MTE didayakan"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dihidupkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dimatikan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lepaskan kekunci kelantangan. Untuk menghidupkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, sila tekan dan tahan kedua-dua kekunci kelantangan sekali lagi selama 3 saat."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Pilih ciri yang hendak digunakan apabila anda mengetik butang kebolehaksesan:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Pilih ciri untuk digunakan dengan gerak isyarat kebolehaksesan (leret ke atas dari bahagian bawah skrin menggunakan dua jari):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Pilih ciri untuk digunakan dengan gerak isyarat kebolehaksesan (leret ke atas dari bahagian bawah skrin menggunakan tiga jari):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Untuk beralih antara ciri, sentuh & tahan butang kebolehaksesan."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Untuk beralih antara ciri, leret ke atas menggunakan dua jari dan tahan."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Untuk beralih antara ciri, leret ke atas menggunakan tiga jari dan tahan."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Pembesaran"</string>
<string name="user_switched" msgid="7249833311585228097">"Pengguna semasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Beralih kepada <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Panggilan sedang berlangsung"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Menyaring panggilan masuk"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Tidak dikategorikan"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promosi"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Sosial"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Berita"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Syor"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Anda menetapkan kepentingan pemberitahuan ini."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Mesej ini penting disebabkan orang yang terlibat."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Pemberitahuan apl tersuai"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Pemilih Pintasan Kebolehaksesan Pada Skrin"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Pintasan Kebolehaksesan"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ketepikan Bidai Pemberitahuan"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Mainkan/Jeda Media"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad Atas"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad Bawah"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Kiri"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index f1ab9e0..f18df96 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ကြိုးမဲ့ အမှားရှာပြင်ခြင်းကို ပိတ်ရန် ရွေးပါ။"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"\'စမ်းသပ်ခြင်းစနစ်မုဒ်\' ဖွင့်ထားသည်"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"\'စမ်းသပ်ခြင်းစနစ် မုဒ်\' ကိုပိတ်ရန် စက်ရုံထုတ်အတိုင်း ပြင်ဆင်သတ်မှတ်ပါ။"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"အမှတ်စဉ် ကွန်ဆိုးလ်ကို ဖွင့်ထားသည်"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"စွမ်းဆောင်ရည်အပေါ် သက်ရောက်မှုရှိနိုင်ပါသည်။ ပိတ်ရန် bootloader ကို စစ်ဆေးပါ။"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"အစမ်းသုံး MTE ကို ဖွင့်ထားသည်"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်လိုက်သည်။"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ပိတ်လိုက်သည်။"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"အသံထိန်းခလုတ်များကို လွှတ်လိုက်ပါ။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်ရန် အသံထိန်းခလုတ်နှစ်ခုစလုံးကို ၃ စက္ကန့်ကြာအောင် ထပ်နှိပ်ပါ။"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"သုံးနိုင်မှုခလုတ်ကို တို့ပြီးလျှင် ဝန်ဆောင်မှုတစ်ခု ရွေးပါ−"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"သုံးနိုင်မှုလက်ဟန်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုတစ်ခုကို ရွေးပါ (ဖန်သားပြင်အောက်ခြေမှနေ၍ လက်နှစ်ချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ)-"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"သုံးနိုင်မှုလက်ဟန်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုတစ်ခုကို ရွေးပါ (ဖန်သားပြင်အောက်ခြေမှနေ၍ လက်သုံးချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ)-"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ဝန်ဆောင်မှုများအကြား ပြောင်းရန် အများသုံးစွဲနိုင်မှုခလုတ်ကို ဖိထားပါ။"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ဝန်ဆောင်မှုများအကြား ပြောင်းရန် လက်နှစ်ချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပြီး ဖိထားပါ။"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ဝန်ဆောင်မှုများအကြား ပြောင်းရန် လက်သုံးချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပြီး ဖိထားပါ။"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ချဲ့ခြင်း"</string>
<string name="user_switched" msgid="7249833311585228097">"လက်ရှိအသုံးပြုနေသူ <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>သို့ ပြောင်းနေသည်…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"လက်ရှိခေါ်ဆိုမှု"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"အဝင်ခေါ်ဆိုမှုကို စစ်ဆေးနေသည်"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"အမျိုးအစားမခွဲရသေးပါ"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"ဤသတိပေးချက်များ၏ အရေးပါမှုကိုသတ်မှတ်ပြီးပါပြီ။"</string>
<string name="importance_from_person" msgid="4235804979664465383">"ပါဝင်သည့်လူများကြောင့် အရေးပါပါသည်။"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"စိတ်ကြိုက်အက်ပ် အကြောင်းကြားချက်"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ဖန်သားပြင်အတွက် အများသုံးစွဲနိုင်မှုဖြတ်လမ်းလင့်ခ် ရွေးချယ်စနစ်"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"အများသုံးနိုင်မှု ဖြတ်လမ်းလင့်ခ်"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"အကြောင်းကြားစာအကွက်ကို ပယ်ရန်"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad အပေါ်"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad အောက်"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ဘယ်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 456366f..dd9bd81 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -268,12 +268,12 @@
<string name="bugreport_title" msgid="8549990811777373050">"Feilrapport"</string>
<string name="bugreport_message" msgid="5212529146119624326">"Informasjon om tilstanden til enheten din samles inn og sendes som en e-post. Det tar litt tid fra du starter feilrapporten til e-posten er klar, så vær tålmodig."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Interaktiv rapport"</string>
- <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Bruk dette alternativet i de fleste tilfeller. Da kan du spore fremgangen for rapporten, skrive inn flere detaljer om problemet samt ta skjermdumper. Noen deler som tar lang tid å behandle, blir kanskje utelatt."</string>
+ <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Bruk dette alternativet i de fleste tilfeller. Da kan du spore fremgangen for rapporten, skrive inn flere detaljer om problemet samt ta skjermbilder. Noen deler som tar lang tid å behandle, blir kanskje utelatt."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Fullstendig rapport"</string>
- <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Bruk dette alternativet for minst mulig forstyrrelse på systemet når enheten din er treg eller ikke svarer, eller når du trenger alle rapportdelene. Det tas ikke noen skjermdump, og du kan ikke legge til flere detaljer."</string>
- <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Tar skjermdump for feilrapporten om # sekund.}other{Tar skjermdump for feilrapporten om # sekunder.}}"</string>
- <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"En skjermdump er tatt med feilrapporten"</string>
- <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Kunne ikke ta skjermdump med feilrapporten"</string>
+ <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Bruk dette alternativet for minst mulig forstyrrelse på systemet når enheten din er treg eller ikke svarer, eller når du trenger alle rapportdelene. Det tas ikke noen skjermbilde, og du kan ikke legge til flere detaljer."</string>
+ <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Tar skjermbilde for feilrapporten om # sekund.}other{Tar skjermbilde for feilrapporten om # sekunder.}}"</string>
+ <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"En skjermbilde er tatt med feilrapporten"</string>
+ <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Kunne ikke ta skjermbilde med feilrapporten"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Stillemodus"</string>
<string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"Lyden er av"</string>
<string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"Lyden er på"</string>
@@ -359,8 +359,8 @@
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Kan trykke, sveipe, klype og gjøre andre bevegelser."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Bevegelser på fingeravtrykkssensor"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Kan fange inn bevegelser som utføres på enhetens fingeravtrykkssensor."</string>
- <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ta skjermdump"</string>
- <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan ikke ta en skjermdump av skjermen."</string>
+ <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Ta skjermbilde"</string>
+ <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Kan ikke ta et skjermbilde av skjermen."</string>
<string name="dream_preview_title" msgid="5570751491996100804">"Forhåndsvisning, <xliff:g id="DREAM_NAME">%1$s</xliff:g>"</string>
<string name="dream_accessibility_action_click" msgid="7392398629967797805">"lukk"</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"deaktivere eller endre statusfeltet"</string>
@@ -509,8 +509,8 @@
<string name="permdesc_recordAudio" msgid="5857246765327514062">"Denne appen kan ta opp lyd med mikrofonen mens den er i bruk."</string>
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ta opp lyd i bakgrunnen"</string>
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Denne appen kan når som helst ta opp lyd med mikrofonen."</string>
- <string name="permlab_detectScreenCapture" msgid="4447042362828799433">"registrere skjermdumper av appvinduer"</string>
- <string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"Denne appen varsles hvis det tas skjermdumper mens appen er i bruk."</string>
+ <string name="permlab_detectScreenCapture" msgid="4447042362828799433">"registrere skjermbilder av appvinduer"</string>
+ <string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"Denne appen varsles hvis det tas skjermbilder mens appen er i bruk."</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"sende kommandoer til SIM-kortet"</string>
<string name="permdesc_sim_communication" msgid="4179799296415957960">"Lar appen sende kommandoer til SIM-kortet. Dette er veldig farlig."</string>
<string name="permlab_activityRecognition" msgid="1782303296053990884">"gjenkjenn fysisk aktivitet"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Velg for å slå av trådløs feilsøking."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Testrammeverk-modus er slått på"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Tilbakestill enheten til fabrikkstandard for å slå av Testrammeverk-modus."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Seriekonsollen er aktivert"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Ytelsen er påvirket. Sjekk oppstartsinnlasteren for å deaktivere."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE på forsøksstadiet er aktivert"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått på."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått av."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Slipp opp volumtastene. For å slå på <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, trykk og hold på begge volumtastene igjen i 3 sekunder."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Velg en funksjon du vil bruke når du trykker på Tilgjengelighet-knappen:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Velg en funksjon du vil bruke med tilgjengelighetsbevegelsen (sveip opp fra bunnen av skjermen med to fingre):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Velg en funksjon du vil bruke med tilgjengelighetsbevegelsen (sveip opp fra bunnen av skjermen med tre fingre):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"For å bytte mellom funksjoner, trykk og hold på Tilgjengelighet-knappen."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"For å bytte mellom funksjoner, sveip opp med to fingre og hold."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"For å bytte mellom funksjoner, sveip opp med tre fingre og hold."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Forstørring"</string>
<string name="user_switched" msgid="7249833311585228097">"Gjeldende bruker: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Bytter til <xliff:g id="NAME">%1$s</xliff:g> …"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pågående samtale"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrerer et innkommende anrop"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Uten kategori"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Du angir viktigheten for disse varslene."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Dette er viktig på grunn av folkene som er involvert."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Tilpasset appvarsel"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Velger for tilgjengelighetssnarvei på skjermen"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Tilgjengelighetssnarvei"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Lukk varselpanelet"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Opp på styrepilene"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Ned på styrepilene"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Venstre på styrepilene"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 55c2ef4..01a6fc7 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"वायरलेस डिबगिङ असक्षम पार्न यो विकल्प चयन गर्नुहोस्।"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"परीक्षण प्याकेज मोड सक्षम पारियो"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"परीक्षण प्याकेज मोड असक्षम पार्न फ्याक्ट्री रिसेट गर्नुहोस्।"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"क्रमसम्बन्धी कन्सोल सक्षम पारियो"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"पर्फर्मेन्समा प्रभाव परेको छ। यसलाई असक्षम पार्न बुटलोडरको जाँच गर्नुहोस्।"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"परीक्षणका क्रममा रहेको MTE अन गरियो"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"भोल्युम बटनहरू थिच्न छाड्नुहोस्। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन गर्न दुवै भोल्युम बटन फेरि ३ सेकेन्डसम्म थिचिराख्नुहोस्।"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"तपाईंले एक्सेसिबिलिटी बटन ट्याप गर्दा प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस्:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (दुईवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (तीनवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एउटा सुविधाबाट अर्को सुविधामा जान पहुँच बटन टच एण्ड होल्ड गर्नुहोस्।"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"एउटा सुविधाबाट अर्को सुविधामा जान दुईवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्।"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एउटा सुविधाबाट अर्को सुविधामा जान तीनवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्।"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"म्याग्निफिकेसन"</string>
<string name="user_switched" msgid="7249833311585228097">"अहिलेको प्रयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>।"</string>
<string name="user_switching_message" msgid="1912993630661332336">"स्विच गरेर <xliff:g id="NAME">%1$s</xliff:g> बनाइँदै..."</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"भइरहेको कल"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"आगमन कल जाँचिँदै छ"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"वर्गीकरण नगरिएको"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"तपाईंले यी सूचनाहरूको महत्त्व सेट गर्नुहोस् ।"</string>
<string name="importance_from_person" msgid="4235804979664465383">"यसमा सङ्लग्न भएका मानिसहरूको कारणले गर्दा यो महत्वपूर्ण छ।"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"एपसम्बन्धी आफ्नो रोजाइअनुसारको सूचना"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"सहज पहुँचका लागि स्क्रिनमा राखिने सर्टकट छान्ने मेनु"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"पहुँचको सर्टकट"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"सूचना कक्ष खारेज गर्नुहोस्"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad को माथिको बटन"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad को तलको बटन"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad को बायाँको बटन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b1b3060..5fdcf85 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecteer deze optie om draadloze foutopsporing uit te zetten."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test harness-modus staat aan"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Reset de fabrieksinstellingen om de test harness-modus uit te zetten."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Seriële console staat aan"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Dit is van invloed op de prestaties. Controleer de bootloader om dit uit te zetten."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimentele MTE aangezet"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> staat aan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> staat uit."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Laat de volumeknoppen los. Als je <xliff:g id="SERVICE_NAME">%1$s</xliff:g> wilt aanzetten, houd je beide volumeknoppen weer 3 seconden ingedrukt."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Kies een functie om te gebruiken als je op de knop Toegankelijkheid tikt:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Kies een functie om te gebruiken met het toegankelijkheidsgebaar (met twee vingers omhoog swipen vanaf de onderkant van het scherm):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Kies een functie om te gebruiken met het toegankelijkheidsgebaar (met drie vingers omhoog swipen vanaf de onderkant van het scherm):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Tik op de knop Toegankelijkheid en houd deze vast om tussen functies te schakelen."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Swipe met twee vingers omhoog en houd vast om tussen functies te schakelen."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Swipe met drie vingers omhoog en houd vast om tussen functies te schakelen."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Vergroting"</string>
<string name="user_switched" msgid="7249833311585228097">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Overschakelen naar <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Actief gesprek"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Een inkomend gesprek screenen"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Geen categorie"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promoties"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Sociaal"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Nieuws"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Aanbevelingen"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Je stelt het belang van deze meldingen in."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Dit is belangrijk vanwege de betrokken mensen."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Aangepaste app-melding"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Kiezer voor snelkoppeling voor toegankelijkheid op scherm"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Snelkoppeling voor toegankelijkheid"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Meldingenpaneel sluiten"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Media afspelen/onderbreken"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"D-pad omhoog"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"D-pad omlaag"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad links"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index b56c114..7c86e97 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ୱାୟାରଲେସ୍ ଡିବଗିଂକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଚୟନ କରନ୍ତୁ।"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"ଟେଷ୍ଟ ହାର୍ନେସ୍ ମୋଡ୍ ସକ୍ଷମ ଅଛି"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"ଟେଷ୍ଟ ହାର୍ନେସ୍ ମୋଡ୍ ଅକ୍ଷମ କରିବାକୁ ଏକ ଫ୍ୟାକ୍ଟରୀ ରିସେଟ୍ କରନ୍ତୁ।"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"କ୍ରମିକ କନ୍ସୋଲ୍କୁ ସକ୍ଷମ କରାଯାଇଛି"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"କାର୍ଯ୍ୟଦକ୍ଷତା ପ୍ରଭାବିତ ହୋଇଛି। ଅକ୍ଷମ କରିବା ପାଇଁ, ବୁଟ୍ଲୋଡର୍ର ଯାଞ୍ଚ କରନ୍ତୁ।"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ପରୀକ୍ଷାମୂଳକ MTEକୁ ସକ୍ଷମ କରାଯାଇଛି"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ଭଲ୍ୟୁମ କୀ\'ଗୁଡ଼ିକୁ ରିଲିଜ କରନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g>କୁ ଚାଲୁ କରିବା ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ କୀ\'କୁ ପୁଣି 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇ ଧରି ରଖନ୍ତୁ।"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ଆପଣ ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ଟାପ୍ କରିବା ବେଳେ ଏକ ଫିଚର୍ ବ୍ୟବହାର କରିବାକୁ ବାଛନ୍ତୁ:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ଆକ୍ସେସିବିଲିଟୀ ଜେଶ୍ଚର୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଏକ ଫିଚର୍ ବାଛନ୍ତୁ (ଦୁଇଟି ଆଙ୍ଗୁଠିରେ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ଆକ୍ସେସିବିଲିଟୀ ଜେଶ୍ଚର୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଏକ ଫିଚର୍ ବାଛନ୍ତୁ (ତିନୋଟି ଆଙ୍ଗୁଠିରେ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ଫିଚରଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ଫିଚରଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ ଦୁଇଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ୍ କରି ଧରି ରଖନ୍ତୁ।"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ଫିଚରଗୁଡ଼ିକ ମଧ୍ୟରେ ସ୍ୱିଚ୍ କରିବାକୁ, ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ୍ କରି ଧରି ରଖନ୍ତୁ।"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ମେଗ୍ନିଫିକେସନ"</string>
<string name="user_switched" msgid="7249833311585228097">"ବର୍ତ୍ତମାନର ୟୁଜର୍ ହେଉଛନ୍ତି <xliff:g id="NAME">%1$s</xliff:g>।"</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>ରେ ସ୍ୱିଚ କରନ୍ତୁ…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"ଚାଲିଥିବା କଲ୍"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ଏକ ଇନକମିଂ କଲକୁ ସ୍କ୍ରିନ୍ କରୁଛି"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"ଅବର୍ଗୀକୃତ"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"ପ୍ରଚାରଗୁଡ଼ିକ"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"ସୋସିଆଲ"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ନ୍ୟୁଜ"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"ସୁପାରିଶଗୁଡ଼ିକ"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ପ୍ରମୁଖତା ଆପଣ ସେଟ୍ କରନ୍ତି।"</string>
<string name="importance_from_person" msgid="4235804979664465383">"ସମ୍ପୃକ୍ତ ଲୋକଙ୍କ କାରଣରୁ ଏହା ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ଅଟେ।"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"କଷ୍ଟମ୍ ଆପ୍ ବିଜ୍ଞପ୍ତି"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ଅନ୍-ସ୍କ୍ରିନ୍ ଆକ୍ସେସିବିଲିଟୀ ସର୍ଟକଟ୍ ବାଛିବା ସୁବିଧା"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ଆକ୍ସେସିବିଲିଟୀ ସର୍ଟକଟ୍"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"ବିଜ୍ଞପ୍ତି ସେଡକୁ ଖାରଜ କରନ୍ତୁ"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"ମେନୁ"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"ମିଡିଆ ପ୍ଲେ କରନ୍ତୁ/ବିରତ କରନ୍ତୁ"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ଉପର"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ତଳ"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ବାମ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 72598d1..a91ec2d9 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"ਵਾਇਰਲੈੱਸ ਡੀਬੱਗਿੰਗ ਨੂੰ ਬੰਦ ਕਰਨ ਲਈ ਚੁਣੋ।"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਚਾਲੂ ਹੈ"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਬੰਦ ਕਰਨ ਲਈ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਕਰੋ।"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"ਸੀਰੀਅਲ ਕੰਸੋਲ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"ਕਾਰਗੁਜ਼ਾਰੀ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਹੈ। ਬੰਦ ਕਰਨ ਲਈ, ਬੂਟਲੋਡਰ ਦੇਖੋ।"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ਪ੍ਰਯੋਗਮਈ MTE ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ ਛੱਡੋ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕਰਨ ਲਈ, ਦੋਵੇਂ ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ 3 ਸਕਿੰਟਾਂ ਲਈ ਦੁਬਾਰਾ ਦਬਾਈ ਰੱਖੋ।"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਟੈਪ ਕੀਤੇ ਜਾਣ \'ਤੇ ਵਰਤਣ ਲਈ ਕੋਈ ਵਿਸ਼ੇਸ਼ਤਾ ਚੁਣੋ:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ਪਹੁੰਚਯੋਗਤਾ ਸੰਕੇਤ ਨਾਲ ਵਰਤਣ ਲਈ ਕੋਈ ਵਿਸ਼ੇਸ਼ਤਾ ਚੁਣੋ (ਦੋ ਉਂਗਲਾਂ ਨਾਲ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ਪਹੁੰਚਯੋਗਤਾ ਸੰਕੇਤ ਨਾਲ ਵਰਤਣ ਲਈ ਕੋਈ ਵਿਸ਼ੇਸ਼ਤਾ ਚੁਣੋ (ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ, ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ \'ਤੇ ਸਪਰਸ਼ ਕਰਕੇ ਰੱਖੋ।"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ, ਦੋ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ।"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ, ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ।"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ਵੱਡਦਰਸ਼ੀਕਰਨ"</string>
<string name="user_switched" msgid="7249833311585228097">"ਮੌਜੂਦਾ ਉਪਭੋਗਤਾ <xliff:g id="NAME">%1$s</xliff:g>।"</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> \'ਤੇ ਸਵਿੱਚ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"ਜਾਰੀ ਕਾਲ"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ਇਨਕਮਿੰਗ ਕਾਲ ਦੀ ਸਕ੍ਰੀਨਿੰਗ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"ਗੈਰ-ਸ਼੍ਰੇਣੀਕਿਰਤ"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"ਪ੍ਰਮੋਸ਼ਨ"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"ਸੋਸ਼ਲ ਐਪਾਂ"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"ਖਬਰਾਂ"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"ਸਿਫ਼ਾਰਸ਼ਾਂ"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ਤੁਸੀਂ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਦੀ ਮਹੱਤਤਾ ਸੈੱਟ ਕੀਤੀ।"</string>
<string name="importance_from_person" msgid="4235804979664465383">"ਇਹ ਸ਼ਾਮਲ ਲੋਕਾਂ ਦੇ ਕਾਰਨ ਮਹੱਤਵਪੂਰਨ ਹੈ।"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"ਵਿਉਂਤੀ ਐਪ ਸੂਚਨਾ"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਣ ਵਾਲੇ ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਦਾ ਚੋਣਕਾਰ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"ਸੂਚਨਾ ਸ਼ੇਡ ਖਾਰਜ ਕਰੋ"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"ਮੀਨੂ"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"ਮੀਡੀਆ ਚਲਾਓ/ਰੋਕੋ"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ਦਾ ਉੱਪਰਲਾ ਬਟਨ"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ਦਾ ਹੇਠਲਾ ਬਟਨ"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ਦਾ ਖੱਬੇ ਪਾਸੇ ਵਾਲਾ ਬਟਨ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index f19b0d0..b1bc748 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -163,7 +163,7 @@
<string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Połączono z szyfrowaną siecią <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
<string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Połączenie przy użyciu karty SIM w sieci <xliff:g id="NETWORK_NAME">%1$s</xliff:g> jest teraz bezpieczniejsze"</string>
<string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Połączono z niezaszyfrowaną siecią"</string>
- <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Gdy korzystasz z karty SIM w sieci <xliff:g id="NETWORK_NAME">%1$s</xliff:g>, połączenia, wiadomości i dane są bardziej narażone na ataki"</string>
+ <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Gdy korzystasz z karty SIM w sieci <xliff:g id="NETWORK_NAME">%1$s</xliff:g>, połączenia, SMS-y i dane są bardziej narażone na ataki"</string>
<string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Połączenia, wiadomości i dane są obecnie bardziej podatne na ataki podczas korzystania z karty SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nGdy połączenie zostanie ponownie zaszyfrowane, otrzymasz kolejne powiadomienie."</string>
<string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Ustawienia bezpieczeństwa sieci komórkowej"</string>
<string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Więcej informacji"</string>
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Wybierz, by wyłączyć debugowanie bezprzewodowe."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Tryb jarzma testowego został włączony"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Przywróć ustawienia fabryczne, by wyłączyć tryb jarzma testowego."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Konsola szeregowa włączona"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Wpływa na wydajność. Aby wyłączyć, sprawdź program rozruchowy."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Włączono eksperymentalne rozszerzenie MTE"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została włączona."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została wyłączona."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Zwolnij przyciski głośności. Aby włączyć usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, naciśnij i przytrzymaj oba przyciski głośności przez 3 sekundy."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Wybierz funkcję używaną po kliknięciu przycisku ułatwień dostępu:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Wybierz funkcję, której chcesz używać w przypadku gestu ułatwień dostępu (przesunięcie dwoma palcami w górę od dołu ekranu):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Wybierz funkcję, której chcesz używać w przypadku gestu ułatwień dostępu (przesunięcie trzema palcami w górę od dołu ekranu):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Aby przełączać funkcje, naciśnij i przytrzymaj przycisk ułatwień dostępu."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Aby przełączać funkcje, przesuń dwoma palcami w górę i przytrzymaj."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Aby przełączyć funkcje, przesuń trzema palcami w górę i przytrzymaj."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Powiększenie"</string>
<string name="user_switched" msgid="7249833311585228097">"Bieżący użytkownik: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Przełączam na użytkownika <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1978,6 +1988,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Trwa połączenie"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtruję połączenie przychodzące"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Bez kategorii"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Ustawiłeś ważność tych powiadomień."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ta wiadomość jest ważna ze względu na osoby uczestniczące w wątku."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Niestandardowe powiadomienie z aplikacji"</string>
@@ -2194,6 +2212,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Wybierz ekranowy skrót ułatwień dostępu"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Skrót ułatwień dostępu"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Zamknij obszar powiadomień"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad – w górę"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad – w dół"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad – w lewo"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index bc6c99c..7b4300a 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1380,7 +1380,7 @@
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar serviço móvel"</string>
<string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Baixe o app da operadora para ativar seu novo chip"</string>
<string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Baixe o app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
- <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Fazer download do app"</string>
+ <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Baixar o app"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo chip inserido"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
<string name="time_picker_dialog_title" msgid="9053376764985220821">"Definir hora"</string>
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecione para desativar a depuração por Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modo Arcabouço de testes ativado"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Console serial ativado"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"O desempenho foi impactado. Para desativar, verifique o carregador de inicialização."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimental ativada"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solte as teclas de volume. Para ativar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, toque e pressione as duas teclas de volume por três segundos."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Escolha um recurso a ser usado quando você toca no botão de acessibilidade:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com três dedos):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para alternar entre recursos, toque no botão de acessibilidade e mantenha-o pressionado."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para alternar entre recursos, deslize de baixo para cima na tela com dois dedos, sem soltar."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para alternar entre recursos, deslize de baixo para cima na tela com três dedos, sem soltar."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ampliação"</string>
<string name="user_switched" msgid="7249833311585228097">"Usuário atual <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Mudando para <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma ligação recebida"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sem classificação"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promoções"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Notícias"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recomendações"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Isso é importante por causa das pessoas envolvidas."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificação personalizada do app"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Seletor de atalho de acessibilidade na tela"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Atalho de acessibilidade"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Dispensar aba de notificações"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Tocar/pausar mídia"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Botão direcional: para cima"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Botão direcional: para baixo"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Botão direcional: para a esquerda"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5cf49fbd..adc1d86 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -663,7 +663,7 @@
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Moveu o dedo demasiado lentamente. Tente novamente."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Experimente outra impressão digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Está demasiado claro"</string>
- <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Detetou-se que o botão ligar/desligar foi premido"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Detetou-se que o botão ligar/desligar foi pressionado"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Experimente ajustar"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Altere a posição do seu dedo ligeiramente de cada vez"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -686,7 +686,7 @@
<string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem sensor de impressões digitais."</string>
<string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporariamente desativado"</string>
<string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Não é possível usar o sensor de impressões digitais. Visite um fornecedor de serviços de reparação."</string>
- <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão ligar/desligar premido"</string>
+ <string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão ligar/desligar pressionado"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar a impressão digital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar o bloqueio de ecrã ou a impressão digital"</string>
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecione para desativar a depuração sem fios."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modo de estrutura de teste ativado"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Efetue uma reposição de dados de fábrica para desativar o Modo de estrutura de teste."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Consola de série ativada"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"O desempenho é afetado. Para desativar, selecione o carregador de arranque."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimental ativada"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas do volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solte as teclas de volume. Para ativar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, prima sem soltar ambas as teclas de volume novamente durante 3 segundos."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Escolha uma funcionalidade para utilizar quando tocar no botão Acessibilidade:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Escolha a funcionalidade a utilizar com o gesto de acessibilidade (deslize rapidamente com dois dedos para cima a partir da parte inferior do ecrã):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Escolha a funcionalidade a utilizar com o gesto de acessibilidade (deslize rapidamente com três dedos para cima a partir da parte inferior do ecrã):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para alternar entre funcionalidades, toque sem soltar no botão Acessibilidade."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para alternar entre funcionalidades, deslize rapidamente com dois dedos para cima sem soltar."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para alternar entre funcionalidades, deslize rapidamente com três dedos para cima sem soltar."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ampliação"</string>
<string name="user_switched" msgid="7249833311585228097">"<xliff:g id="NAME">%1$s</xliff:g> do utilizador atual."</string>
<string name="user_switching_message" msgid="1912993630661332336">"A mudar para <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em curso"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"A filtrar uma chamada recebida…"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sem categoria"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promoções"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Redes sociais"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Notícias"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recomendações"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Definiu a importância destas notificações."</string>
<string name="importance_from_person" msgid="4235804979664465383">"É importante devido às pessoas envolvidas."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificação de app personalizada"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selecionador de atalhos de acessibilidade no ecrã"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Atalho de acessibilidade"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ignorar o painel de notificações"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Reproduzir/pausar multimédia"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Teclado direcional: para cima"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Teclado direcional: para baixo"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Teclado direcional: para a esquerda"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bc6c99c..7b4300a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1380,7 +1380,7 @@
<string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar serviço móvel"</string>
<string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Baixe o app da operadora para ativar seu novo chip"</string>
<string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Baixe o app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
- <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Fazer download do app"</string>
+ <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Baixar o app"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo chip inserido"</string>
<string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
<string name="time_picker_dialog_title" msgid="9053376764985220821">"Definir hora"</string>
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selecione para desativar a depuração por Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modo Arcabouço de testes ativado"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Console serial ativado"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"O desempenho foi impactado. Para desativar, verifique o carregador de inicialização."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimental ativada"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solte as teclas de volume. Para ativar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, toque e pressione as duas teclas de volume por três segundos."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Escolha um recurso a ser usado quando você toca no botão de acessibilidade:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com dois dedos):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Escolha um recurso para usar com o gesto de acessibilidade (deslizar de baixo para cima na tela com três dedos):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para alternar entre recursos, toque no botão de acessibilidade e mantenha-o pressionado."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para alternar entre recursos, deslize de baixo para cima na tela com dois dedos, sem soltar."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para alternar entre recursos, deslize de baixo para cima na tela com três dedos, sem soltar."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ampliação"</string>
<string name="user_switched" msgid="7249833311585228097">"Usuário atual <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Mudando para <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma ligação recebida"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Sem classificação"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promoções"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Social"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Notícias"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recomendações"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Isso é importante por causa das pessoas envolvidas."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificação personalizada do app"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Seletor de atalho de acessibilidade na tela"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Atalho de acessibilidade"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Dispensar aba de notificações"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Tocar/pausar mídia"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Botão direcional: para cima"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Botão direcional: para baixo"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Botão direcional: para a esquerda"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index e4a6a07..e718f64 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selectează pentru a dezactiva remedierea erorilor wireless."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modul Set de testare este activat"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Revino la setările din fabrică pentru a dezactiva modul Set de testare."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Consola din serie este activată"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performanța este afectată. Pentru a dezactiva, verifică programul bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimentală activată"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Eliberează butoanele de volum. Pentru a activa <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, apasă lung pe ambele butoane de volum timp de trei secunde încă o dată."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Alege o funcție pe care să o folosești când atingi butonul de accesibilitate:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Alege o funcție pe care să o folosești cu gestul de accesibilitate (glisează în sus cu două degete din partea de jos a ecranului):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Alege o funcție pe care să o folosești cu gestul de accesibilitate (glisează în sus cu trei degete din partea de jos a ecranului):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Pentru a comuta între funcții, atinge lung butonul de accesibilitate."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Pentru a comuta între funcții, glisează în sus cu două degete și ține apăsat."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Pentru a comuta între funcții, glisează în sus cu trei degete și ține apăsat."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Mărire"</string>
<string name="user_switched" msgid="7249833311585228097">"Utilizator curent: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Se comută la <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Apel în desfășurare"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Se filtrează un apel primit"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Neclasificate"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promoțiii"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Rețele sociale"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Știri"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Recomandări"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Tu setezi importanța acestor notificări."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Notificarea este importantă având în vedere persoanele implicate."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificare de aplicație personalizată"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selector de comenzi rapide de accesibilitate de pe ecran"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Comandă rapidă de accesibilitate"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Închide fereastra de notificări"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Meniu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Redă / întrerupe conținutul media"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad sus"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad jos"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad stânga"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0e98b3f..45f93ef 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Нажмите, чтобы отключить отладку по Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Тестовый режим включен"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Чтобы отключить тестовый режим, сбросьте настройки до заводских."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Консоль последовательного порта включена"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Производительность устройства снижена. Чтобы отключить консоль, перейдите в загрузчик операционной системы."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Включена экспериментальная функция MTE"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Отпустите кнопки громкости. Чтобы включить <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, нажмите и удерживайте обе кнопки регулировки громкости в течение трех секунд."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Выберите функцию, которая будет запускаться при нажатии кнопки специальных возможностей:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Выберите функцию, которая будет запускаться с помощью жеста (провести по экрану снизу вверх двумя пальцами):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Выберите функцию, которая будет запускаться с помощью жеста (провести по экрану снизу вверх тремя пальцами):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Для переключения между функциями нажмите и удерживайте кнопку специальных возможностей."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Для переключения между функциями проведите по экрану снизу вверх двумя пальцами и задержите их."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Для переключения между функциями проведите по экрану снизу вверх тремя пальцами и задержите их."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Увеличение"</string>
<string name="user_switched" msgid="7249833311585228097">"Выбран аккаунт пользователя <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Смена профиля на \"<xliff:g id="NAME">%1$s</xliff:g>\"…"</string>
@@ -1978,6 +1988,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Текущий вызов"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Фильтрация входящего вызова"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Без категории"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Вы определяете важность этих уведомлений."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Важное (люди)"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Уведомление пользовательского приложения"</string>
@@ -2194,6 +2212,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Выбор действия для быстрого включения"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Быстрое включение"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Скрыть панель уведомлений"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"D-pad – вверх"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"D-pad – вниз"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad – влево"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 03068fe..ae30a64 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"නොරැහැන් නිදොස්කරණය අබල කිරීමට තෝරන්න."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"පුරක පරීක්ෂා ප්රකාරය සබලයි"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"පුරක පරීක්ෂා ප්රකාරය අබල කිරීමට කර්මාන්තශාලා යළි සැකසීමක් ඉටු කරන්න."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"අනුක්රමික කොන්සෝලය සබලයි"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"කාර්ය සාධනය බලපෑමට ලක් වී ඇත. අබල කිරීමට, ආරම්භකය පරීක්ෂා කරන්න."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"අත්හදා බැලීමේ MTE සබලයි"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාත්මකයි."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාවිරහිතයි."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"හඬ පරිමා යතුරු මුදා හරින්න. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> සක්රීය කිරීමට, හඬ පරිමා යතුරු දෙකම නැවත තත්පර 3ක් ඔබා අල්ලා සිටින්න."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ඔබ ප්රවේශ්යතා බොත්තම තට්ටු කරන විට භාවිත කිරීමට විශේෂාංගයක් තෝරා ගන්න:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ප්රවේශ්යතා ඉංගිතය සමඟ භාවිතයට විශේෂාංගයක් තෝරා ගන්න (ඇඟිලි දෙකකින් තිරයේ පහළ සිට ඉහළට ස්වයිප් කරන්න):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ප්රවේශ්යතා ඉංගිතය සමඟ භාවිතයට විශේෂාංගයක් තෝරා ගන්න (ඇඟිලි තුනකින් තිරයේ පහළ සිට ඉහළට ස්වයිප් කරන්න):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"විශේෂාංග අතර මාරු වීමට, ප්රවේශ්යතා බොත්තම ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"විශේෂාංග අතර මාරු වීමට, ඇඟිලි දෙකකින් ඉහළට ස්වයිප් කර අල්ලාගෙන සිටින්න."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"විශේෂාංග අතර මාරු වීමට, ඇඟිලි තුනකින් ඉහළට ස්වයිප් කර අල්ලාගෙන සිටින්න."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"විශාලනය"</string>
<string name="user_switched" msgid="7249833311585228097">"දැනට සිටින පරිශීලකයා <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> වෙත මාරු කරමින්…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"කරගෙන යන ඇමතුම"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"එන ඇමතුමක් පරීක්ෂා කරන්න"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"වර්ගීකරණය නොකළ"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"ප්රවර්ධන"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"සමාජයීය"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"පුවත්"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"නිර්දේශ"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ඔබ මෙම දැනුම්දීම්වල වැදගත්කම සකසා ඇත."</string>
<string name="importance_from_person" msgid="4235804979664465383">"සම්බන්ධ වූ පුද්ගලයන් නිසා මෙය වැදගත් වේ."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"අභිරුචි යෙදුම් දැනුම් දීම"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"තිරය මත ප්රවේශ්යතා කෙටිමං තෝරනය"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ප්රවේශ්යතා කෙටිමඟ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"දැනුම්දීම් සෙවන ඉවත ලන්න"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"මෙනුව"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"මාධ්ය වාදනය/විරාමය"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ඉහළ"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad පහළ"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad වම"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 694c55bb..0368c13 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Výberom zakážete bezdrôtové ladenie."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Režim správcu testov je aktivovaný"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Ak chcete zakázať režim správcu testov, obnovte výrobné nastavenia."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Sériová konzola je povolená"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Ovplyvňuje výkon. Ak ju chcete zakázať, skontrolujte zavádzací program systému."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Je zapnuté experimentálne rozšírenie MTE"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Uvoľnite tlačidlá hlasitosti. Ak chcete zapnúť službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, znova pridržte tri sekundy obe tlačidlá hlasitosti."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Klepnutím na tlačidlo dostupnosti vyberte požadovanú funkciu:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Vyberte funkciu, ktorú chcete používať s daným gestom dostupnosti (potiahnutím dvoma prstami z dolnej časti obrazovky nahor):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Vyberte funkciu aktivovanú daným gestom dostupnosti (potiahnutím troma prstami z dolnej časti obrazovky nahor):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funkcie prepnete pridržaním tlačidla dostupnosti."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funkcie prepnete potiahnutím dvoma prstami nahor a pridržaním."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funkcie prepnete potiahnutím troma prstami nahor a pridržaním."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Zväčšenie"</string>
<string name="user_switched" msgid="7249833311585228097">"Aktuálny používateľ je <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Prepína sa na účet <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1978,6 +1988,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Prebiehajúci hovor"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Preveruje sa prichádzajúci hovor"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Nekategorizované"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promá"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Sociálne siete"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Správy"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Odporúčania"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Nastavili ste dôležitosť týchto upozornení."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Táto správa je dôležitá vzhľadom na osoby, ktorých sa to týka."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Vlastné upozornenie na aplikáciu"</string>
@@ -2194,6 +2208,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Výber skratky dostupnosti na obrazovke"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Skratka dostupnosti"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Zavrieť panel upozornení"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Ponuka"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Prehrať / pozastaviť médium"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Stlačiť tlačidlo nahor krížového ovládača"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Stlačiť tlačidlo nadol krížového ovládača"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Stlačiť tlačidlo doľava krížového ovládača"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index edf80c5..2da83ec 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Izberite, če želite onemogočiti brezžično odpravljanje napak."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Način preizkusnega ogrodja je omogočen"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Če želite onemogočiti način preizkusnega ogrodja, ponastavite napravo na tovarniške nastavitve."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola je omogočena"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Učinkovitost delovanja je slabša. Uporabo konzole lahko onemogočite v zagonskem nalagalniku."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Preizkusne razširitve MTE so omogočene"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Spustite gumba za glasnost. Če želite vklopiti storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, znova pritisnite in 3 sekunde pridržite oba gumba za glasnost."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Izberite funkcijo, ki jo želite uporabljati, ko se dotaknete gumba za dostopnost:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Izberite funkcijo, ki jo želite zagnati s potezo za dostopnost (vlečenje z dvema prstoma z dna zaslona navzgor):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Izberite funkcijo, ki jo želite zagnati s potezo za dostopnost (vlečenje s tremi prsti z dna zaslona navzgor):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Če želite preklopiti med funkcijami, pridržite gumb za dostopnost."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Če želite preklopiti med funkcijami, z dvema prstoma povlecite navzgor in pridržite."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Če želite preklopiti med funkcijami, s tremi prsti povlecite navzgor in pridržite."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Povečava"</string>
<string name="user_switched" msgid="7249833311585228097">"Trenutni uporabnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Preklapljanje na uporabnika <xliff:g id="NAME">%1$s</xliff:g> …"</string>
@@ -1978,6 +1988,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Aktivni klic"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Preverjanje dohodnega klica"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Nekategorizirano"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Vi določite raven pomembnosti teh obvestil."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Pomembno zaradi udeleženih ljudi."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Obvestilo po meri iz aplikacije"</string>
@@ -2194,6 +2212,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Izbirnik zaslonske bližnjice za dostopnost"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Bližnjica za dostopnost"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Opusti zaslon z obvestili"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Smerni gumb gor"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Smerni gumb dol"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Smerni gumb levo"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 88c4987..4b026fd 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Zgjidh për të çaktivizuar korrigjimin përmes Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modaliteti i lidhjes së testimit është aktivizuar"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Kryej një rivendosje në cilësimet e fabrikës për të çaktivizuar \"Modalitetin e lidhjes së testimit\"."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Paneli komandues i serisë është aktivizuar"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Ndikohet cilësia e funksionimit. Për ta çaktivizuar, kontrollo ngarkuesin e sistemit."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE eksperimentale u aktivizua"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lësho tastet e volumit. Për të aktivizuar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, shtyp dhe mbaj shtypur të dy tastet e volumit sërish për 3 sekonda."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Zgjidh një funksion për ta përdorur kur troket butonin e qasshmërisë:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Zgjidh një veçori për ta përdorur me gjestin e qasshmërisë (rrëshqit shpejt lart nga fundi i ekranit me dy gishta):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Zgjidh një veçori për ta përdorur me gjestin e qasshmërisë (rrëshqit shpejt lart nga fundi i ekranit me tre gishta):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Për të kaluar mes veçorive, prek dhe mbaj prekur butonin e qasshmërisë."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Për të kaluar mes veçorive, rrëshqit shpejt lart me dy gishta dhe mbaje prekur."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Për të kaluar mes veçorive, rrëshqit shpejt lart me tre gishta dhe mbaje prekur."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Zmadhimi"</string>
<string name="user_switched" msgid="7249833311585228097">"Emri i përdoruesit aktual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="user_switching_message" msgid="1912993630661332336">"Po kalon në \"<xliff:g id="NAME">%1$s</xliff:g>\"…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Telefonatë në vazhdim"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Po filtron një telefonatë hyrëse"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"E pakategorizuara"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Ke caktuar rëndësinë e këtyre njoftimeve."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Është i rëndësishëm për shkak të personave të përfshirë."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Njoftim i personalizuar për aplikacionin"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Zgjedhësi i shkurtores së qasshmërisë në ekran"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Shkurtorja e qasshmërisë"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Hiq \"Strehën e njoftimeve\""</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Lart në bllokun e drejtimit"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Poshtë në bllokun e drejtimit"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Majtas në bllokun e drejtimit"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f620246..cb37cc3 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1412,6 +1412,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Изаберите да бисте онемогућили бежично отклањање грешака."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Омогућен је режим пробног коришћења"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Обавите ресетовање на фабричка подешавања да бисте онемогућили режим пробног коришћења."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Серијска конзола је омогућена"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Перформансе су смањене. Да бисте онемогући конзолу, проверите покретачки програм."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Експериментални MTE је омогућен"</string>
@@ -1756,12 +1760,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Пустите тастере за јачину звука. Да бисте укључили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, поново притисните и задржите оба тастера за јачину звука 3 секунде."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Изаберите функцију која ће се користити када додирнете дугме Приступачност:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Одаберите функцију која ће се користити помоћу покрета за приступачност (помоћу два прста превуците нагоре од дна екрана):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Одаберите функцију која ће се користити помоћу покрета за приступачност (помоћу три прста превуците нагоре од дна екрана):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Да бисте прелазили са једне функције на другу, додирните и задржите дугме Приступачност."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Да бисте прелазили са једне функције на другу, превуците нагоре помоћу два прста и задржите."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Да бисте прелазили са једне функције на другу, превуците нагоре помоћу три прста и задржите."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Увећање"</string>
<string name="user_switched" msgid="7249833311585228097">"Актуелни корисник <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Пребацивање на <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1977,6 +1987,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Позив је у току"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Проверава се долазни позив"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Некатегоризовано"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Промоције"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Друштвене мреже"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Вести"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Препоруке"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Ви подешавате важност ових обавештења."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Ово је важно због људи који учествују."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Прилагођено обавештење о апликацији"</string>
@@ -2193,6 +2207,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Алатка за бирање пречица за приступачност на екрану"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Пречица за приступачност"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Одбаци траку са обавештењима"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Мени"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Пусти/паузирај медије"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"нагоре на D-pad-у"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"надоле на D-pad-у"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"налево на D-pad-у"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 35c4c71..06d32b8 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Välj för att inaktivera trådlös felsökning."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Läget för testverktyg har aktiverats"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Inaktivera testverktygsläget genom att göra en återställning till standardinställningarna."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Seriekonsolen är aktiverad"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Prestandan påverkas. Inaktivera via starthanteraren."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Experimentell MTE har aktiverats"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Släpp volymknapparna. Du kan aktivera <xliff:g id="SERVICE_NAME">%1$s</xliff:g> genom att hålla båda volymknapparna nedtryckta i tre sekunder igen."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Välj en funktion som ska användas när du trycker på tillgänglighetsknappen:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Välj en funktion som ska användas med tillgänglighetsrörelsen (svepa uppåt med två fingrar från skärmens nederkant):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Välj en funktion som ska användas med tillgänglighetsrörelsen (svepa uppåt med tre fingrar från skärmens nederkant):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Byt mellan funktionerna genom att trycka länge på tillgänglighetsknappen."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Byt funktion genom att svepa uppåt med två fingrar och hålla kvar dem."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Byt funktion genom att svepa uppåt med tre fingrar och hålla kvar dem."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Förstoring"</string>
<string name="user_switched" msgid="7249833311585228097">"Nuvarande användare: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Byter till <xliff:g id="NAME">%1$s</xliff:g> …"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pågående samtal"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Ett inkommande samtal filtreras"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Okategoriserad"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Kampanjer"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Socialt"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Nyheter"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Rekommendationer"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Du anger hur viktiga aviseringarna är."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Detta är viktigt på grund av personerna som deltar."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Anpassad appavisering"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Valfunktion för tillgänglighetsgenväg på skärmen"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Aktivera tillgänglighet snabbt"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Stäng meddelandepanelen"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Meny"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Spela upp/pausa"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Styrkors, upp"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Styrkors, ned"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Styrkors, vänster"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index dc19026..bb1a363 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1318,7 +1318,7 @@
<string name="volume_call" msgid="7625321655265747433">"Sauti ya simu inayoendelea"</string>
<string name="volume_bluetooth_call" msgid="2930204618610115061">"Sauti ya simu inayoendelea ya Bluetooth"</string>
<string name="volume_alarm" msgid="4486241060751798448">"Sauti ya kengele"</string>
- <string name="volume_notification" msgid="6864412249031660057">"Sauti ya notisi"</string>
+ <string name="volume_notification" msgid="6864412249031660057">"Sauti ya arifa"</string>
<string name="volume_unknown" msgid="4041914008166576293">"Kiwango"</string>
<string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"Sauti ya Bluetooth"</string>
<string name="volume_icon_description_ringer" msgid="2187800636867423459">"Sauti ya toni mlio"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Chagua ili uzime utatuzi usiotumia waya."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Hali ya Muunganisho wa Majaribio imewashwa"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Rejesha mipangilio iliyotoka nayo kiwandani ili uzime hali ya Muunganisho wa Majaribio."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Muunganisho kupitia mlango umewashwa"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Utendaji unaathirika. lli uzime, teua programu ya kuwasha mfumo wa uendeshaji."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Kipengele cha majaribio cha MTE kimeruhusiwa"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Achilia vitufe vya sauti. Ili uwashe <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, bonyeza na ushikilie tena vitufe vyote vya sauti kwa sekunde 3."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Chagua kipengele utakachotumia ukigusa kitufe cha ufikivu:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Chagua kipengele cha kutumia pamoja na ishara ya ufikivu (telezesha vidole viwili kutoka chini kwenda juu kwenye skrini):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Chagua kipengele cha kutumia pamoja na ishara ya ufikivu (telezesha vidole vitatu kutoka chini kwenda juu kwenye skrini):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Ili ubadilishe kati ya vipengele, gusa na ushikilie kitufe cha zana za ufikivu."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Ili ubadilishe kati ya vipengele, telezesha vidole viwili juu na ushikilie."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Ili ubadilishe kati ya vipengele, telezesha vidole vitatu juu na ushikilie."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ukuzaji"</string>
<string name="user_switched" msgid="7249833311585228097">"Mtumiaji wa sasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Inaenda kwa <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Simu inayoendelea"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Inachuja simu unayopigiwa"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Ambazo aina haijabainishwa"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Matoleo"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Mitandao Jamii"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Habari"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Mapendekezo"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Uliweka mipangilio ya umuhimu wa arifa hizi."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Hii ni muhimu kwa sababu ya watu waliohusika."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Arifa ya programu maalum"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Kichagua Njia ya Mkato ya Ufikivu kwenye Skrini"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Njia ya Mkato ya Ufikivu"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Ondoa Sehemu ya Arifa"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menyu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Cheza/Sitisha Maudhui"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Kitufe cha juu cha Dpad"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Kitufe cha chini cha Dpad"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Kitufe cha kushoto cha Dpad"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 91ae904..8246af8 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"வைஃபை பிழைதிருத்தத்தை முடக்க தேர்ந்தெடுக்கவும்."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"\'தன்னியக்க சோதனைப்\' பயன்முறை இயக்கப்பட்டது"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"’தன்னியக்க சோதனைப்\' பயன்முறையை முடக்க ஆரம்பநிலைக்கு மீட்டமைக்கவும்."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"சீரியல் கன்சோல் இயக்கப்பட்டது"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"செயல்திறன் பாதிக்கப்பட்டுள்ளது. முடக்குவதற்கு பூட்லோடரைத் தேர்வுசெய்யவும்."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"பரிசோதனை MTE இயக்கப்பட்டது"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ஒலியளவு பட்டன்களை அழுத்துவதை நிறுத்துங்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> சேவையை இயக்க, ஒலியளவு பட்டன்கள் இரண்டையும் 3 வினாடிகளுக்கு மீண்டும் அழுத்திப் பிடிக்கவும்."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"அணுகல்தன்மை பட்டனைத் தட்டுவதன் மூலம் பயன்படுத்த விரும்பும் அம்சத்தைத் தேர்ந்தெடுக்கவும்:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"அணுகல்தன்மை சைகைக்கான அம்சத்தைத் தேர்வுசெய்யவும் (இரண்டு விரல்களால் திரையின் கீழிருந்து மேல் நோக்கி ஸ்வைப் செய்யவும்):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"அணுகல்தன்மை சைகைக்கான அம்சத்தைத் தேர்வுசெய்யவும் (மூன்று விரல்களால் திரையின் கீழிருந்து மேல் நோக்கி ஸ்வைப் செய்யவும்):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"அம்சங்களுக்கு இடையே மாற அணுகல்தன்மை பட்டனைத் தொட்டுப் பிடித்திருக்கவும்."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"அம்சங்களுக்கு இடையே மாற இரண்டு விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடித்திருக்கவும்."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"அம்சங்களுக்கு இடையே மாற மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடித்திருக்கவும்."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"பெரிதாக்கல்"</string>
<string name="user_switched" msgid="7249833311585228097">"நடப்பு பயனர் <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g>க்கு மாறுகிறது…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"செயலில் இருக்கும் அழைப்பு"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"உள்வரும் அழைப்பை மதிப்பாய்வு செய்கிறது"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"வகைப்படுத்தப்படாதவை"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"இந்த அறிவிப்புகளின் முக்கியத்துவத்தை அமைத்துள்ளீர்கள்."</string>
<string name="importance_from_person" msgid="4235804979664465383">"ஈடுபட்டுள்ளவர்களின் காரணமாக, இது முக்கியமானது."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"பிரத்தியேக ஆப்ஸ் அறிவிப்பு"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"திரையிலுள்ள அணுகல்தன்மை ஷார்ட்கட்டிற்கான தேர்வி"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"அணுகல்தன்மை ஷார்ட்கட்"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"அறிவிப்பு விவரத்தை நிராகரி"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"மேல் திசை காட்டும் பட்டன்"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"கீழ் திசை காட்டும் பட்டன்"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"இடது திசை காட்டும் பட்டன்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 759c6e6..749a5d6 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"వైర్లెస్ డీబగ్గింగ్ను డిజేబుల్ చేయడానికి ఎంచుకోండి."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"పరీక్ష నియంత్రణ మోడ్ ప్రారంభించబడింది"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"పరీక్ష నియంత్రణ మోడ్ను నిలిపివేయడానికి ఫ్యాక్టరీ రీసెట్ను అమలు చేయండి."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"సీరియల్ కన్సోల్ ప్రారంభించబడింది"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"పని తీరు ప్రభావితమైంది. నిలిపివేయడానికి, బూట్లోడర్ను చెక్ చేయండి."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ప్రయోగాత్మక MTE ఎనేబుల్ చేయబడింది"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"వాల్యూమ్ కీలను రిలీజ్ చేయండి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ను ఆన్ చేయడానికి, రెండు వాల్యూమ్ కీలను మళ్లీ 3 సెకన్ల పాటు నొక్కి పట్టుకోండి."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"యాక్సెస్ సామర్థ్య బటన్ను మీరు నొక్కినప్పుడు ఉపయోగించాల్సిన ఒక ఫీచర్ను ఎంచుకోండి:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"యాక్సెసిబిలిటీ సంజ్ఞతో ఉపయోగించడానికి ఒక ఫీచర్ని ఎంచుకోండి (రెండు వేళ్లతో స్క్రీన్ను కింద నుండి పైకి స్వైప్ చేయండి):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"యాక్సెసిబిలిటీ సంజ్ఞతో ఉపయోగించడానికి ఒక ఫీచర్ను ఎంచుకోండి (మూడు చేతి వేళ్లతో స్క్రీన్ను కింద నుండి పైకి స్వైప్ చేయండి):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"ఫీచర్ల మధ్య మారడానికి, యాక్సెసిబిలిటీ బటన్ను నొక్కి & పట్టుకోండి."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"ఫీచర్ల మధ్య మారడానికి, రెండు చేతి వేళ్ళతో పైకి స్వైప్ చేసి పట్టుకోండి."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"ఫీచర్ల మధ్య మారడానికి, మూడు చేతి వేళ్ళతో పైకి స్వైప్ చేసి పట్టుకోండి."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"మ్యాగ్నిఫికేషన్"</string>
<string name="user_switched" msgid="7249833311585228097">"ప్రస్తుత వినియోగదారు <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> యూజర్కు స్విచ్ అవుతోంది…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"కాల్ కొనసాగుతోంది"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"ఇన్కమింగ్ కాల్ను స్క్రీన్ చేయండి"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"వర్గీకరించబడలేదు"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"ప్రమోషన్లు"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"సామాజికం"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"వార్తలు"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"సిఫార్సులు"</string>
<string name="importance_from_user" msgid="2782756722448800447">"మీరు ఈ నోటిఫికేషన్ల ప్రాముఖ్యతను సెట్ చేశారు."</string>
<string name="importance_from_person" msgid="4235804979664465383">"ఇందులో పేర్కొనబడిన వ్యక్తులను బట్టి ఇది చాలా ముఖ్యమైనది."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"అనుకూల యాప్ నోటిఫికేషన్"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"స్క్రీన్పై ఉండే యాక్సెసిబిలిటీ షార్ట్కట్ల ఎంపిక సాధనం"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"యాక్సెసిబిలిటీ షార్ట్కట్"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"నోటిఫికేషన్ తెరను తీసివేయండి"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"మెనూ"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"మీడియాను ప్లే/పాజ్ చేయండి"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad పైకి"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad కింద"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ఎడమవైపున"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 282f66e..f27155f 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"เลือกเพื่อปิดใช้การแก้ไขข้อบกพร่องผ่าน Wi-Fi"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"โหมดโปรแกรมทดสอบอัตโนมัติเปิดใช้อยู่"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"รีเซ็ตเป็นค่าเริ่มต้นเพื่อปิดใช้โหมดโปรแกรมทดสอบอัตโนมัติ"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"เปิดใช้คอนโซลการเรียงอันดับแล้ว"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"ประสิทธิภาพได้รับผลกระทบ ตรวจสอบ Bootloader เพื่อปิดใช้งาน"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"เปิดใช้ MTE เชิงทดสอบอยู่"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว เปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว ปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ปล่อยปุ่มปรับระดับเสียง หากต้องการเปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ให้กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้อีกครั้งเป็นเวลา 3 วินาที"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"เลือกฟีเจอร์ที่จะใช้เมื่อคุณแตะปุ่มการช่วยเหลือพิเศษดังต่อไปนี้"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"เลือกฟีเจอร์ที่จะใช้กับท่าทางสัมผัสการช่วยเหลือพิเศษ (ใช้ 2 นิ้วเลื่อนขึ้นจากด้านล่างของหน้าจอ) ดังต่อไปนี้"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"เลือกฟีเจอร์ที่จะใช้กับท่าทางสัมผัสการช่วยเหลือพิเศษ (ใช้ 3 นิ้วเลื่อนขึ้นจากด้านล่างของหน้าจอ) ดังต่อไปนี้"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"หากต้องการสลับระหว่างฟีเจอร์ต่างๆ ให้แตะปุ่มการช่วยเหลือพิเศษค้างไว้"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"หากต้องการสลับระหว่างฟีเจอร์ต่างๆ ให้ใช้ 2 นิ้วเลื่อนขึ้นแล้วค้างไว้"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"หากต้องการสลับระหว่างฟีเจอร์ต่างๆ ให้ใช้ 3 นิ้วเลื่อนขึ้นแล้วค้างไว้"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"การขยาย"</string>
<string name="user_switched" msgid="7249833311585228097">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="user_switching_message" msgid="1912993630661332336">"กำลังเปลี่ยนเป็น<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"สายที่สนทนาอยู่"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"กำลังสกรีนสายเรียกเข้า"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"ไม่จัดอยู่ในหมวดหมู่ใดๆ"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"คุณตั้งค่าความสำคัญของการแจ้งเตือนเหล่านี้"</string>
<string name="importance_from_person" msgid="4235804979664465383">"ข้อความนี้สำคัญเนื่องจากบุคคลที่เกี่ยวข้อง"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"การแจ้งเตือนที่กำหนดเองของแอป"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"ตัวเลือกทางลัดการช่วยเหลือพิเศษบนหน้าจอ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ทางลัดการช่วยเหลือพิเศษ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"ปิดหน้าต่างแจ้งเตือน"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad ขึ้น"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad ลง"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ซ้าย"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 31b81aa..ecaf004 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Piliin para i-disable ang wireless na pag-debug."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Naka-enable ang Test Harness Mode"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Mag-factory reset para i-disable ang Test Harness Mode."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Naka-enable ang serial console"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Naaapektuhan ang performance. Para i-disable, lagyan ng check ang bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Na-enable ang Pang-eksperimentong MTE"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Bitawan ang mga volume key. Para i-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, muling pindutin nang matagal ang dalawang volume key sa loob ng 3 segundo."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Pumili ng feature na gagana sa pamamagitan ng pag-tap mo sa button ng accessibility:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Pumili ng feature na gagana sa pamamagitan ng galaw ng accessibility (pag-swipe pataas mula sa ibaba ng screen gamit ang dalawang daliri):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Pumili ng feature na gagana sa pamamagitan ng galaw ng accessibility (pag-swipe pataas mula sa ibaba ng screen gamit ang tatlong daliri):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Para magpalipat-lipat sa mga feature, pindutin nang matagal ang button ng accessibility."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Para magpalipat-lipat sa mga feature, mag-swipe pataas gamit ang dalawang daliri at i-hold ito."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Para magpalipat-lipat sa mga feature, mag-swipe pataas gamit ang tatlong daliri at i-hold ito."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Pag-magnify"</string>
<string name="user_switched" msgid="7249833311585228097">"Kasalukuyang user <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Lumilipat kay <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Kasalukuyang tawag"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Nagsi-screen ng papasok na tawag"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Di-nakategorya"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Ikaw ang magtatakda sa kahalagahan ng mga notification na ito."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Mahalaga ito dahil sa mga taong kasangkot."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Custom na notification ng app"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Tagapili ng Shortcut ng Accessibility sa Screen"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Shortcut ng Accessibility"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"I-dismiss ang Notification Shade"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad Up"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad Down"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 13f7c2f..6c94899 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Kablosuz hata ayıklamayı devre dışı bırakmak için seçin."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Test Bandı Modu etkin"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Test Bandı Modu\'nu devre dışı bırakmak için cihazı fabrika ayarlarına sıfırlayın."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Seri konsol etkinleştirildi"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performans etkilendi. Devre dışı bırakmak için bootloader\'ı kontrol edin."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Deneysel MTE etkinleştirildi"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Ses seviyesi tuşlarını bırakın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini etkinleştirmek için her iki ses seviyesi tuşuna yeniden basıp 3 saniye boyunca basılı tutun."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Erişilebilirlik düğmesine dokunduğunuzda kullanmak için bir özellik seçin:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Erişilebilirlik hareketiyle (iki parmakla ekranın altından yukarı kaydırma) kullanılacak bir özellik seçin:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Erişilebilirlik hareketiyle (üç parmakla ekranın altından yukarı kaydırma) kullanılacak bir özellik seçin:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Özellikler arasında geçiş yapmak için erişilebilirlik düğmesine dokunup basılı tutun."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Özellikler arasında geçiş yapmak için iki parmakla yukarı kaydırıp basılı tutun."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Özellikler arasında geçiş yapmak için üç parmakla yukarı kaydırıp basılı tutun."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Büyütme"</string>
<string name="user_switched" msgid="7249833311585228097">"Geçerli kullanıcı: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> adlı kullanıcıya geçiliyor…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Devam eden arama"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Gelen arama süzülüyor"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Kategorize edilmemiş"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Promosyonlar"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Sosyal"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Haberler"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Öneriler"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Bu bildirimlerin önem derecesini ayarladınız."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Bu, dahil olan kişiler nedeniyle önemlidir."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Özel uygulama bildirimi"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekran Erişilebilirlik Kısayol Seçici"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Erişilebilirlik Kısayolu"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Bildirim Gölgesini Kapat"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Menü"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Medyayı Oynat/Duraklat"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad Yukarı"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad Aşağı"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Sol"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index b8764e7..d34e801 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -158,7 +158,7 @@
<string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Безпека мобільної мережі"</string>
<string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Шифрування, сповіщення для незашифрованих мереж"</string>
<string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Отримано доступ до ідентифікатора пристрою"</string>
- <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"О <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> у мережі поблизу зафіксовано унікальний ідентифікатор вашого пристрою (IMSI або IMEI) під час використання вашої SIM-карти <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
+ <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"О <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, коли ви використали SIM-карту <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>, мережа поблизу зафіксувала унікальний ідентифікатор вашого пристрою (IMSI або IMEI)"</string>
<string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"О <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> у мережі поблизу зафіксовано унікальний ідентифікатор вашого пристрою (IMSI або IMEI) під час використання вашої SIM-карти <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nЦе означає, що було зареєстровано ваше місцезнаходження, дії чи особу. Це звичайна практика, але може виявитися проблемою для людей, для яких важлива конфіденційність."</string>
<string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Підключено до зашифрованої мережі <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
<string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Тепер з’єднання SIM-карти <xliff:g id="NETWORK_NAME">%1$s</xliff:g> краще захищене"</string>
@@ -1413,6 +1413,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Натисніть, щоб вимкнути налагодження."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Увімкнено режим автоматизованого тестування"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Щоб вимкнути режим автоматизованого тестування, відновіть заводські налаштування."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Послідовну консоль увімкнено"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Продуктивність зазнала впливу. Щоб вимкнути, перевірте завантажувач операційної системи."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Експериментальний запуск з MTE ввімкнено"</string>
@@ -1757,12 +1761,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Відпустіть клавіші гучності. Щоб увімкнути сервіс <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, натисніть і втримуйте обидві клавіші гучності протягом 3 секунд."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Виберіть функцію для кнопки спеціальних можливостей:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Виберіть функцію для жесту спеціальних можливостей (проведення двома пальцями знизу вгору):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Виберіть функцію для жесту спеціальних можливостей (проведення трьома пальцями знизу вгору):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Щоб переключитися між функціями, натисніть і утримуйте кнопку спеціальних можливостей."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Щоб переключитися між функціями, проведіть по екрану знизу вгору двома пальцями й утримуйте їх."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Щоб переключитися між функціями, проведіть по екрану знизу вгору трьома пальцями й утримуйте їх."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Збільшення"</string>
<string name="user_switched" msgid="7249833311585228097">"Поточний користувач: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Перехід у режим \"<xliff:g id="NAME">%1$s</xliff:g>\"…"</string>
@@ -1978,6 +1988,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Активний виклик"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Вхідний виклик (Фільтр)"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Без категорії"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Ви вказуєте пріоритет цих сповіщень."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Важливе з огляду на учасників."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Користувацьке сповіщення додатка"</string>
@@ -2194,6 +2212,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Вибір екранного засобу спеціальних можливостей"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Засіб спеціальних можливостей"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Закрити панель сповіщень"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Кнопка \"вгору\" панелі керування"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Кнопка \"вниз\" панелі керування"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Кнопка \"вліво\" панелі керування"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 1ae5e20..a67752b 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"وائرلیس ڈیبگنگ کو غیر فعال کرنے کے ليے منتخب کریں۔"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"ٹیسٹ ہارنیس موڈ فعال ہے"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"ٹیسٹ ہارنیس موڈ غیر فعال کرنے کے لیے فیکٹری ری سیٹ کریں۔"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"شمار کونسول فعال ہے"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"کارکردگی پر اثر پڑا ہے۔ غیر فعال کرنے کے ليے، بوٹ لوڈر چیک کریں۔"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"تجرباتی MTE کو فعال کیا گیا"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"والیوم کی کلیدوں کو ریلیز کریں <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آن کرنے کے لیے، والیوم کی دونوں کلیدوں کو دوبارہ 3 سیکنڈ تک چھوئیں اور دبائے رکھیں۔"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ایکسیسبیلٹی بٹن پر تھپتھپانے وقت استعمال کرنے کیلئے ایک خصوصیت منتخب کریں:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ایکسیسبیلٹی اشارہ کے ساتھ استعمال کرنے کے لیے ایک خصوصیت چنیں (دو انگلیوں سے اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ایکسیسبیلٹی اشارہ کے ساتھ استعمال کرنے کے لیے ایک خصوصیت چنیں (تین انگلیوں سے اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"خصوصیات کے مابین سوئچ کرنے کے لیے، ایکسیسبیلٹی بٹن کو ٹچ کریں اور دبائے رکھیں۔"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"خصوصیات کے مابین سوئچ کرنے کے لیے، دو انگلیوں سے اوپر سوائپ کریں اور دبائیں رکھیں۔"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"خصوصیات کے مابین سوئچ کرنے کے لیے، تین انگلیوں سے اوپر سوائپ کریں اور دبائیں رکھیں۔"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"میگنیفکیشن"</string>
<string name="user_switched" msgid="7249833311585228097">"موجودہ صارف <xliff:g id="NAME">%1$s</xliff:g>۔"</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> پر سوئچ کیا جا رہا ہے…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"جاری کال"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"اِن کمنگ کال کی اسکریننگ"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"غیر زمرہ بند"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"پروموشنز"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"سوشل"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"خبریں"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"تجاویز"</string>
<string name="importance_from_user" msgid="2782756722448800447">"ان اطلاعات کی اہمیت آپ مقرر کرتے ہیں۔"</string>
<string name="importance_from_person" msgid="4235804979664465383">"اس میں موجود لوگوں کی وجہ سے یہ اہم ہے۔"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"حسب ضرورت ایپ کی اطلاع"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"آن اسکرین ایکسیسبیلٹی شارٹ کٹ منتخب کنندہ"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ایکسیسبیلٹی کا شارٹ کٹ"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"اطلاعاتی شیڈ برخاست کریں"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"مینیو"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"میڈیا چلائیں/موقوف کریں"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad اوپر کریں"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad نیچے کریں"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad بائیں کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 8d6e169..dfc510c 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Uni faolsizlantirish uchun bosing."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Xavfsizlik sinovi rejimi yoqildi"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Xavfsizlik sinovi rejimini faolsizlantirish uchun zavod sozlamalariga qaytaring."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Ketma-ket port konsoli yoqildi"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Qurilma samaradorligi pasaydi. Konsolni faolsizlantirish uchun operatsion tizim yuklagichini oching."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Tajribaviy MTE yoqildi"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> yoqildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> faolsizlantirildi."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Tovush tugmalarini qoʻyib yuboring. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatini yoqish uchun ikkala tovush tugmasini 3 soniya bosib turing."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Qulayliklar tugmasi bosilganda ishga tushadigan funksiyani tanlang:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Qulayliklar ishorasi bilan ishga tushadigan funksiyani tanlang (ikkita barmoq bilan ekranning pastidan tepaga surib tortilganda):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Qulayliklar ishorasi bilan ishga tushadigan funksiyani tanlang (uchta barmoq bilan ekranning pastidan tepaga surib tortilganda):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalarni almashtirish uchun maxsus imkoniyatlar tugmasini bosib turing."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funksiyalarni almashtirish uchun ikkita barmoq bilan tepaga suring va ushlab turing."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funksiyalarni almashtirish uchun uchta barmoq bilan tepaga suring va ushlab turing."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Kattalashtirish"</string>
<string name="user_switched" msgid="7249833311585228097">"Joriy foydalanuvchi <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Bunga almashilmoqda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Joriy chaqiruv"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Kiruvchi chaqiruvni filtrlash"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Turkumlanmagan"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Siz ushbu bildirishnomalarning muhimligini belgilagansiz."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Bu odamlar siz uchun muhim."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Maxsus ilova bildirishnomasi"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekranda tezkor ishga tushirishni tanlagich"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Tezkor ishga tushirish"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Eslatma soyasini yopish"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad – tepaga"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad – pastga"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad – chapga"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 857a095..7388926 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Chọn để tắt tính năng gỡ lỗi qua Wi-Fi."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Đã bật Chế độ khai thác kiểm thử"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Khôi phục cài đặt gốc để tắt Chế độ khai thác kiểm thử."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"Đã bật bảng điều khiển cổng nối tiếp"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Hiệu suất sẽ bị ảnh hưởng. Để tắt, hãy chọn trình tải khởi động."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"Đã bật MTE thử nghiệm"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Thả phím âm lượng. Để bật <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, hãy nhấn và giữ cả 2 phím âm lượng trong 3 giây một lần nữa."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Chọn một tính năng để dùng khi bạn nhấn nút hỗ trợ tiếp cận:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Chọn một tính năng để dùng với cử chỉ hỗ trợ tiếp cận (dùng 2 ngón tay vuốt lên từ cuối màn hình):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Chọn một tính năng để dùng với cử chỉ hỗ trợ tiếp cận (dùng 3 ngón tay vuốt lên từ cuối màn hình):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Để chuyển đổi giữa các tính năng, hãy chạm và giữ nút hỗ trợ tiếp cận."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Để chuyển đổi giữa các tính năng, hãy dùng 2 ngón tay vuốt lên và giữ."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Để chuyển đổi giữa các tính năng, hãy dùng 3 ngón tay vuốt lên và giữ."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Phóng to"</string>
<string name="user_switched" msgid="7249833311585228097">"Người dùng hiện tại <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Đang chuyển sang <xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Cuộc gọi đang thực hiện"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Đang sàng lọc cuộc gọi đến"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Chưa được phân loại"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"Bạn đặt tầm quan trọng của các thông báo này."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Thông báo này quan trọng vì những người có liên quan."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Thông báo tùy chỉnh cho ứng dụng"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Bộ chọn lối tắt hỗ trợ tiếp cận trên màn hình"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Lối tắt hỗ trợ tiếp cận"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Đóng Ngăn thông báo"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Chuyển lên trên bằng bàn phím di chuyển"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Chuyển xuống dưới bằng bàn phím di chuyển"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Chuyển sang trái bằng bàn phím di chuyển"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6cd89cf..c84c16c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"选择即可停用无线调试功能。"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"自动化测试框架模式已启用"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"恢复出厂设置以停用自动化测试框架模式。"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"已启用序列控制台"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"性能受到影响。要停用,请查看引导加载程序。"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"已启用实验性 MTE"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已开启。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已关闭。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"松开音量键。如要启用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>,请再次同时按住两个音量键 3 秒。"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"选择点按“无障碍”按钮后要使用的功能:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"选择要搭配无障碍手势(用两根手指从屏幕底部向上滑动)使用的功能:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"选择要搭配无障碍手势(用三根手指从屏幕底部向上滑动)使用的功能:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"轻触并按住“无障碍”按钮,即可在多项功能之间切换。"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"用两根手指向上滑动并按住,即可在多项功能之间切换。"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"用三根手指向上滑动并按住,即可在多项功能之间切换。"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"放大功能"</string>
<string name="user_switched" msgid="7249833311585228097">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string>
<string name="user_switching_message" msgid="1912993630661332336">"正在切换为<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"正在通话"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"正在过滤来电"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"未分类"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"这些通知的重要程度由您来设置。"</string>
<string name="importance_from_person" msgid="4235804979664465383">"这条通知涉及特定的人,因此被归为重要通知。"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"自定义应用通知"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"屏幕上的无障碍功能快捷方式选择器"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"无障碍功能快捷方式"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"关闭通知栏"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"向上方向键"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"向下方向键"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"向左方向键"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 5224753..b229f27 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"選取即可停用無線偵錯功能。"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"已啟用測試工具模式"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"請將裝置回復原廠設定,以停用測試工具模式。"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"已啟用序列控制器"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"效能受到影響,勾選啟動程式即可停用。"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"實驗版 MTE 已啟用"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已開啟。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已關閉。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"鬆開音量鍵。如果要開 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>,請同時㩒住兩個音量鍵 3 秒。"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"請選擇輕按「無障礙功能」按鈕時使用的功能:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"請選擇要配搭無障礙手勢 (使用兩隻手指從螢幕底部向上滑動) 使用的功能:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"請選擇要配搭無障礙手勢 (使用三隻手指從螢幕底部向上滑動) 使用的功能:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"如要在功能之間切換,請按住無障礙功能按鈕。"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"如要在功能之間切換,請使用兩隻手指向上滑動並按住。"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"如要在功能之間切換,請使用三隻手指向上滑動並按住。"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"放大"</string>
<string name="user_switched" msgid="7249833311585228097">"目前的使用者是<xliff:g id="NAME">%1$s</xliff:g>。"</string>
<string name="user_switching_message" msgid="1912993630661332336">"正在切換至<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"正在過濾來電"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"未分類"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"你可以設定這些通知的重要性。"</string>
<string name="importance_from_person" msgid="4235804979664465383">"列為重要的原因:涉及的人。"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"自訂應用程式通知"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"螢幕無障礙功能捷徑選擇器"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"無障礙功能捷徑"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"關閉通知欄"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"十字鍵向上鍵"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"十字鍵向下鍵"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"十字鍵向左鍵"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index fe66eb9..13c3ff78 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -160,9 +160,9 @@
<string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"在 <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>,鄰近網路使用你的「<xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>」SIM 卡時,記錄了你的裝置專屬 ID (IMSI 或 IMEI)。\n\n這代表鄰近網路記錄了你的位置、活動或身分資訊。儘管這種情況很常見,但注重隱私的人可能會認為這是個問題。"</string>
<string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"已連上加密網路「<xliff:g id="NETWORK_NAME">%1$s</xliff:g>」"</string>
<string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"現在連上「<xliff:g id="NETWORK_NAME">%1$s</xliff:g>」SIM 卡更加安全"</string>
- <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"已連上未加密網路"</string>
- <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"目前使用「<xliff:g id="NETWORK_NAME">%1$s</xliff:g>」SIM 卡時,通話、訊息和資料較容易受到攻擊"</string>
- <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"目前使用「<xliff:g id="NETWORK_NAME">%1$s</xliff:g>」SIM 卡時,通話、訊息和資料較容易受到攻擊。\n\n連線再次加密時,你會收到另一則通知。"</string>
+ <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"連接上未加密網路"</string>
+ <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"使用目前的「<xliff:g id="NETWORK_NAME">%1$s</xliff:g>」SIM 卡,通話、訊息和資料較易受到攻擊"</string>
+ <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"使用目前的「<xliff:g id="NETWORK_NAME">%1$s</xliff:g>」SIM 卡,通話、訊息和資料較易受到攻擊。\n\n連線再次加密時,你會收到另一則通知。"</string>
<string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"行動網路安全性設定"</string>
<string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"瞭解詳情"</string>
<string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"我知道了"</string>
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"選取即可停用無線偵錯功能。"</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"測試控管工具模式已啟用"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"恢復原廠設定以停用測試控管工具模式。"</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"已啟用序列主控台"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"效能已受到影響。如要停用,請檢查系統啟動載入程式。"</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"已啟用實驗 MTE"</string>
@@ -1433,7 +1437,7 @@
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"輕觸即可選取語言和版面配置"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"顯示在其他應用程式上層"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"在其他應用程式上面顯示"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"「<xliff:g id="NAME">%s</xliff:g>」在其他應用程式上顯示內容"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"「<xliff:g id="NAME">%s</xliff:g>」正在其他應用程式上顯示內容"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"如果你不想讓「<xliff:g id="NAME">%s</xliff:g>」使用這項功能,請輕觸開啟設定頁面,然後停用此功能。"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已開啟。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已關閉。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"放開音量鍵。如要開啟 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>,請同時按住音量調高鍵和調低鍵 3 秒。"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"輕觸無障礙工具按鈕後,選擇你想使用的功能:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"選擇要搭配無障礙手勢 (用兩指從螢幕底部向上滑動) 使用的功能:"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"選擇要搭配無障礙手勢 (用三指從螢幕底部向上滑動) 使用的功能:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"如要切換不同的功能,請按住無障礙工具按鈕。"</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"如要切換不同的功能,請用兩指向上滑動並按住。"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"如要切換不同的功能,請用三指向上滑動並按住。"</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"放大"</string>
<string name="user_switched" msgid="7249833311585228097">"目前的使用者是 <xliff:g id="NAME">%1$s</xliff:g>。"</string>
<string name="user_switching_message" msgid="1912993630661332336">"正在切換至<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,14 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"正在過濾來電"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"未分類"</string>
+ <!-- no translation found for promotional_notification_channel_label (7414844730492860233) -->
+ <skip />
+ <!-- no translation found for social_notification_channel_label (106520267132019945) -->
+ <skip />
+ <!-- no translation found for news_notification_channel_label (4299937455247883311) -->
+ <skip />
+ <!-- no translation found for recs_notification_channel_label (4945985121418684297) -->
+ <skip />
<string name="importance_from_user" msgid="2782756722448800447">"這些通知的重要性由你決定。"</string>
<string name="importance_from_person" msgid="4235804979664465383">"這則通知涉及特定人士,因此被歸為重要通知。"</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"自訂應用程式通知"</string>
@@ -2192,6 +2210,10 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"螢幕上的無障礙捷徑選擇器"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"無障礙捷徑"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"關閉通知欄"</string>
+ <!-- no translation found for accessibility_system_action_menu_label (3385283204496447040) -->
+ <skip />
+ <!-- no translation found for accessibility_system_action_media_play_pause_label (1905647491347119748) -->
+ <skip />
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad 向上移"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad 向下移"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad 向左移"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 8285d3e..53b5507 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1411,6 +1411,10 @@
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Khetha ukukhubaza ukulungisa amaphutha okungenantambo."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Imodi yokuhlola i-harness inikwe amandla"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Yenza ukusetha kabusha kwasekuqaleni ukuze ukhubaze imodi yokuqina yokuhlola."</string>
+ <!-- no translation found for wrong_hsum_configuration_notification_title (7212758829332714385) -->
+ <skip />
+ <!-- no translation found for wrong_hsum_configuration_notification_message (5353475441480684381) -->
+ <skip />
<string name="console_running_notification_title" msgid="6087888939261635904">"I-serial console inikwe amandla"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Ukusebenza kuyathinteka. Ukuze ukhubaze, hlola i-bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"I-Experimental MTE inikwe amandla"</string>
@@ -1755,12 +1759,18 @@
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Khipha okhiye bevolumu. Ukuze uvule i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>, cindezela bese ubamba bobabili okhiye bevolumu futhi imizuzwana emi-3."</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Khetha isici ozosisebenzisa uma uthepha inkinobho yokufinyelela:"</string>
- <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Khetha isici ozosisebenzisa ngokuthinta kokufinyeleleka (swayiphela phezulu kusukela ngaphansi kwesikrini ngeminwe emibili):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Khetha isici ozosisebenzisa ngokuthinta kokufinyeleleka (swayiphela phezulu kusukela phansi esikrinini ngeminwe emithathu):"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Ukuze ushintshe phakathi kwezici, thinta uphinde ubambe inkinobho yokufinyeleleka."</string>
- <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Ukuze ushintshe phakathi kwezici, swayiphela phezulu ngeminwe emibili uphinde ubambe."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Ukuze ushintshe phakathi kwezici, swayiphela phezulu ngeminwe emithathu uphinde ubambe."</string>
+ <!-- no translation found for accessibility_button_prompt_text (6105393217162198616) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_prompt_text (6452246951969541792) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_prompt_text (77745752309056152) -->
+ <skip />
+ <!-- no translation found for accessibility_button_instructional_text (8029780800681458835) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_instructional_text (1485998586929977949) -->
+ <skip />
+ <!-- no translation found for accessibility_gesture_3finger_instructional_text (3430237316928654219) -->
+ <skip />
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Ukukhuliswa"</string>
<string name="user_switched" msgid="7249833311585228097">"Umsebenzisi wamanje <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Ishintshela ku-<xliff:g id="NAME">%1$s</xliff:g>…"</string>
@@ -1976,6 +1986,10 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ikholi eqhubekayo"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Ukuveza ikholi engenayo"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Akufakwanga esigabeni"</string>
+ <string name="promotional_notification_channel_label" msgid="7414844730492860233">"Izindali"</string>
+ <string name="social_notification_channel_label" msgid="106520267132019945">"Imithombo yezokuxhumana"</string>
+ <string name="news_notification_channel_label" msgid="4299937455247883311">"Izindaba"</string>
+ <string name="recs_notification_channel_label" msgid="4945985121418684297">"Izincomo"</string>
<string name="importance_from_user" msgid="2782756722448800447">"Usethe ukubaluleka kwalezi zaziso."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Lokhu kubalulekile ngenxa yabantu ababandakanyekayo."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Isaziso sohlelo lokusebenza olungokwezifiso"</string>
@@ -2192,6 +2206,8 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Isikhethi sesinqamuleli sokufinyeleleka kusikrini"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Isinqamuleli sokufinyeleleka"</string>
<string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Cashisa Umthunzi Wesaziso"</string>
+ <string name="accessibility_system_action_menu_label" msgid="3385283204496447040">"Imenyu"</string>
+ <string name="accessibility_system_action_media_play_pause_label" msgid="1905647491347119748">"Ukudlala/Ukumisa Imidiya"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Phezulu kwe-Dpad"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Phansi kwe-Dpad"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Ngakwesokunxele se-Dpad"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9846b71..4883827 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1361,7 +1361,7 @@
<!-- @hide -->
<attr name="customColorThemeAppRing" format="color"/>
<!-- @hide -->
- <attr name="customColorOnThemeAppRing" format="color"/>
+ <attr name="customColorThemeNotif" format="color"/>
<!-- @hide -->
<attr name="customColorBrandA" format="color"/>
<!-- @hide -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 5e039b5..37412a0 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -590,7 +590,7 @@
<color name="system_theme_app_light">#2F4578</color>
<color name="system_on_theme_app_light">#D6DFFF</color>
<color name="system_theme_app_ring_light">#94AAE4</color>
- <color name="system_on_theme_app_ring_light">#FDD7FA</color>
+ <color name="system_theme_notif_light">#FDD7FA</color>
<color name="system_brand_a_light">#3A5084</color>
<color name="system_brand_b_light">#6E7488</color>
<color name="system_brand_c_light">#6076AC</color>
@@ -611,7 +611,7 @@
<color name="system_theme_app_dark">#D9E2FF</color>
<color name="system_on_theme_app_dark">#304679</color>
<color name="system_theme_app_ring_dark">#94AAE4</color>
- <color name="system_on_theme_app_ring_dark">#E0BBDD</color>
+ <color name="system_theme_notif_dark">#E0BBDD</color>
<color name="system_brand_a_dark">#90A6DF</color>
<color name="system_brand_b_dark">#A4ABC1</color>
<color name="system_brand_c_dark">#7A90C8</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4e133de..335b740 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -567,14 +567,6 @@
It has been updated to affect other plug types. -->
<bool name="config_keepDreamingWhenUnplugging">false</bool>
- <!-- The timeout (in ms) to wait before attempting to reconnect to the dream overlay service if
- it becomes disconnected -->
- <integer name="config_dreamOverlayReconnectTimeoutMs">1000</integer> <!-- 1 second -->
- <!-- The maximum number of times to attempt reconnecting to the dream overlay service -->
- <integer name="config_dreamOverlayMaxReconnectAttempts">3</integer>
- <!-- The duration after which the dream overlay connection should be considered stable -->
- <integer name="config_minDreamOverlayDurationMs">10000</integer> <!-- 10 seconds -->
-
<!-- Auto-rotation behavior -->
<!-- If true, enables auto-rotation features using the accelerometer.
@@ -2633,10 +2625,7 @@
If false, not supported. -->
<bool name="config_duplicate_port_omadm_wappush">false</bool>
- <!-- Maximum numerical value that will be shown in a status bar
- notification icon or in the notification itself. Will be replaced
- with @string/status_bar_notification_info_overflow when shown in the
- UI. -->
+ <!-- @deprecated No longer used. -->
<integer name="status_bar_notification_info_maxnum">999</integer>
<!-- Path to an ISO image to be shared with via USB mass storage.
@@ -3917,6 +3906,7 @@
<item>countdown</item>
<item>schedule</item>
<item>event</item>
+ <item>custom_manual</item>
</string-array>
<!-- Priority repeat caller threshold, in minutes -->
@@ -5807,6 +5797,9 @@
<!-- The action name for the default profcollect report uploader app. -->
<string name="config_defaultProfcollectReportUploaderAction" translatable="false"></string>
+ <!-- Names of packages excluded by Profcollect onCameraOpened observer. -->
+ <string-array name="config_profcollectOnCameraOpenedSkipPackages" translatable="false"></string-array>
+
<!-- The default value used for RawContacts.ACCOUNT_NAME when contacts are inserted without this
column set. These contacts are stored locally on the device and will not be removed even
if no android.account.Account with this name exists. A null string will be used if the
@@ -6969,6 +6962,9 @@
Note that, indefinitely repeating vibrations are not allowed as shutdown vibrations. -->
<string name="config_defaultShutdownVibrationFile" />
+ <!-- Whether or not vibration is disabled during shutdown -->
+ <bool name="config_disableShutdownVibrationInZen">false</bool>
+
<!-- Whether single finger panning is enabled by default when magnification is on -->
<bool name="config_enable_a11y_magnification_single_panning">false</bool>
@@ -7085,6 +7081,9 @@
<!-- Whether the system uses auto-suspend mode. -->
<bool name="config_useAutoSuspend">true</bool>
+ <!-- Whether close/dismiss buttons are supported on notifications. -->
+ <bool name="config_notificationCloseButtonSupported">false</bool>
+
<!-- Whether to show GAIA education screen during account login of private space setup.
OEM/Partner can explicitly opt to disable the screen. -->
<bool name="config_enableGaiaEducationInPrivateSpace">true</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 5fea515..6cba84b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -355,6 +355,9 @@
<!-- the padding of the expand icon in the notification header -->
<dimen name="notification_expand_button_icon_padding">2dp</dimen>
+ <!-- the size of the notification close button -->
+ <dimen name="notification_close_button_size">16dp</dimen>
+
<!-- Vertical margin for the headerless notification content, when content has 1 line -->
<!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
<dimen name="notification_headerless_margin_oneline">16dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b6e8383..fa93e76 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -788,11 +788,7 @@
<!-- label for item that locks the phone and enforces that it can't be unlocked without strong authentication. [CHAR LIMIT=24] -->
<string name="global_action_lockdown">Lockdown</string>
- <!-- Text to use when the number in a notification info is too large
- (greater than status_bar_notification_info_maxnum, defined in
- values/config.xml) and must be truncated. May need to be localized
- for most appropriate textual indicator of "more than X".
- [CHAR LIMIT=4] -->
+ <!-- @deprecated No longer used. -->
<string name="status_bar_notification_info_overflow">999+</string>
<!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
@@ -3837,6 +3833,11 @@
<!-- Message of notification shown when Test Harness Mode is enabled. [CHAR LIMIT=NONE] -->
<string name="test_harness_mode_notification_message">Perform a factory reset to disable Test Harness Mode.</string>
+ <!-- Title of notification shown when device is in the wrong Headless System User Mode configuration. [CHAR LIMIT=NONE] -->
+ <string name="wrong_hsum_configuration_notification_title">Wrong HSUM build configuration</string>
+ <!-- Message of notification shown when device is in the wrong Headless System User Mode configuration. [CHAR LIMIT=NONE] -->
+ <string name="wrong_hsum_configuration_notification_message">The Headless System User Mode state of this device differs from its build configuration. Please factory reset the device.</string>
+
<!-- Title of notification shown when serial console is enabled. [CHAR LIMIT=NONE] -->
<string name="console_running_notification_title">Serial console enabled</string>
<!-- Message of notification shown when serial console is enabled. [CHAR LIMIT=NONE] -->
@@ -4844,19 +4845,19 @@
<!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_spoken_feedback">Release the volume keys. To turn on <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g>, press and hold both volume keys again for 3 seconds.</string>
- <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. [CHAR LIMIT=none]-->
- <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the accessibility button:</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar or in gesture navigation. [CHAR LIMIT=none]-->
+ <string name="accessibility_button_prompt_text">Choose a feature</string>
<!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled [CHAR LIMIT=none] -->
- <string name="accessibility_gesture_prompt_text">Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):</string>
+ <string name="accessibility_gesture_prompt_text">Choose a feature</string>
<!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled [CHAR LIMIT=none] -->
- <string name="accessibility_gesture_3finger_prompt_text">Choose a feature to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):</string>
+ <string name="accessibility_gesture_3finger_prompt_text">Choose a feature</string>
<!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. [CHAR LIMIT=none]-->
- <string name="accessibility_button_instructional_text">To switch between features, touch & hold the accessibility button.</string>
+ <string name="accessibility_button_instructional_text">The feature will open next time you tap the accessibility button</string>
<!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled. [CHAR LIMIT=none] -->
- <string name="accessibility_gesture_instructional_text">To switch between features, swipe up with two fingers and hold.</string>
+ <string name="accessibility_gesture_instructional_text">The feature will open next time you use this shortcut. Swipe up with 2 fingers from the bottom of your screen and release quickly.</string>
<!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled. [CHAR LIMIT=none] -->
- <string name="accessibility_gesture_3finger_instructional_text">To switch between features, swipe up with three fingers and hold.</string>
+ <string name="accessibility_gesture_3finger_instructional_text">The feature will open next time you use this shortcut. Swipe up with 3 fingers from the bottom of your screen and release quickly.</string>
<!-- Text used to describe system navigation features, shown within a UI allowing a user to assign system magnification features to the Accessibility button in the navigation bar. -->
<string name="accessibility_magnification_chooser_text">Magnification</string>
@@ -5430,6 +5431,10 @@
<string name="call_notification_screening_text">Screening an incoming call</string>
<string name="default_notification_channel_label">Uncategorized</string>
+ <string name="promotional_notification_channel_label">Promotions</string>
+ <string name="social_notification_channel_label">Social</string>
+ <string name="news_notification_channel_label">News</string>
+ <string name="recs_notification_channel_label">Recommendations</string>
<string name="importance_from_user">You set the importance of these notifications.</string>
<string name="importance_from_person">This is important because of the people involved.</string>
@@ -5981,6 +5986,10 @@
<string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string>
<!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string>
+ <!-- Label for menu action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_menu_label">Menu</string>
+ <!-- Label for media play/pause action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_media_play_pause_label">Media Play/Pause</string>
<!-- Label for Dpad up action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_dpad_up_label">Dpad Up</string>
<!-- Label for Dpad down action [CHAR LIMIT=NONE] -->
@@ -6514,4 +6523,15 @@
<string name="biometric_dangling_notification_action_set_up">Set up</string>
<!-- Biometric dangling notification "Not now" action button -->
<string name="biometric_dangling_notification_action_not_now">Not now</string>
+
+
+ <!-- Background user sound notification related messages -->
+ <!-- Notification title when sound comes from an alarm or timer on background user [CHAR LIMIT=NOTIF_TITLE]-->
+ <string name="bg_user_sound_notification_title_alarm">Alarm for <xliff:g id="user_name" example="John Doe">%s</xliff:g></string>
+ <!-- Notification action button to prompt user switch to the background user [CHAR LIMIT=NONE] -->
+ <string name="bg_user_sound_notification_button_switch_user">Switch user</string>
+ <!-- Notification action button to mute the sound from the background user [CHAR LIMIT=NONE] -->
+ <string name="bg_user_sound_notification_button_mute">Mute</string>
+ <!-- Notification text to mute the sound from the background user [CHAR LIMIT=NOTIF_BODY]-->
+ <string name="bg_user_sound_notification_message">Tap to mute sound</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7251d74..bdcf13c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -521,6 +521,7 @@
<java-symbol type="bool" name="config_preferKeepClearForFocus" />
<java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/>
<java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/>
+ <java-symbol type="bool" name="config_notificationCloseButtonSupported"/>
<java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/>
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -2152,6 +2153,8 @@
<java-symbol type="string" name="adbwifi_active_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_message" />
+ <java-symbol type="string" name="wrong_hsum_configuration_notification_title" />
+ <java-symbol type="string" name="wrong_hsum_configuration_notification_message" />
<java-symbol type="string" name="console_running_notification_title" />
<java-symbol type="string" name="console_running_notification_message" />
<java-symbol type="string" name="mte_override_notification_title" />
@@ -2298,9 +2301,6 @@
<java-symbol type="array" name="config_disabledDreamComponents" />
<java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
<java-symbol type="bool" name="config_resetScreenTimeoutOnUnexpectedDreamExit" />
- <java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" />
- <java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" />
- <java-symbol type="integer" name="config_minDreamOverlayDurationMs" />
<java-symbol type="array" name="config_loggable_dream_prefixes" />
<java-symbol type="string" name="config_dozeComponent" />
<java-symbol type="string" name="enable_explore_by_touch_warning_title" />
@@ -3171,6 +3171,7 @@
<java-symbol type="id" name="header_text_secondary_divider" />
<java-symbol type="drawable" name="ic_expand_notification" />
<java-symbol type="drawable" name="ic_collapse_notification" />
+ <java-symbol type="drawable" name="notification_close_button_icon" />
<java-symbol type="drawable" name="ic_expand_bundle" />
<java-symbol type="drawable" name="ic_collapse_bundle" />
<java-symbol type="drawable" name="ic_notification_summary_auto" />
@@ -3877,6 +3878,7 @@
<java-symbol type="string" name="expand_button_content_description_collapsed" />
<java-symbol type="string" name="expand_button_content_description_expanded" />
+ <java-symbol type="string" name="close_button_text" />
<java-symbol type="string" name="content_description_collapsed" />
<java-symbol type="string" name="content_description_expanded" />
@@ -4444,6 +4446,7 @@
<java-symbol type="bool" name="config_profcollectReportUploaderEnabled" />
<java-symbol type="string" name="config_defaultProfcollectReportUploaderApp" />
<java-symbol type="string" name="config_defaultProfcollectReportUploaderAction" />
+ <java-symbol type="array" name="config_profcollectOnCameraOpenedSkipPackages" />
<java-symbol type="string" name="usb_device_resolve_prompt_warn" />
@@ -4461,6 +4464,8 @@
<java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" />
<java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" />
<java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" />
+ <java-symbol type="string" name="accessibility_system_action_menu_label" />
+ <java-symbol type="string" name="accessibility_system_action_media_play_pause_label" />
<java-symbol type="string" name="accessibility_system_action_dpad_up_label" />
<java-symbol type="string" name="accessibility_system_action_dpad_down_label" />
<java-symbol type="string" name="accessibility_system_action_dpad_left_label" />
@@ -5041,6 +5046,9 @@
<java-symbol type="string" name="ui_translation_accessibility_translation_finished" />
<java-symbol type="layout" name="notification_expand_button"/>
+ <java-symbol type="id" name="close_button" />
+ <java-symbol type="layout" name="notification_close_button"/>
+ <java-symbol type="id" name="notification_buttons_column" />
<java-symbol type="bool" name="config_supportsMicToggle" />
<java-symbol type="bool" name="config_supportsCamToggle" />
@@ -5299,7 +5307,7 @@
<java-symbol name="customColorThemeApp" type="attr"/>
<java-symbol name="customColorOnThemeApp" type="attr"/>
<java-symbol name="customColorThemeAppRing" type="attr"/>
- <java-symbol name="customColorOnThemeAppRing" type="attr"/>
+ <java-symbol name="customColorThemeNotif" type="attr"/>
<java-symbol name="customColorBrandA" type="attr"/>
<java-symbol name="customColorBrandB" type="attr"/>
<java-symbol name="customColorBrandC" type="attr"/>
@@ -5321,7 +5329,7 @@
<java-symbol name="system_theme_app_light" type="color"/>
<java-symbol name="system_on_theme_app_light" type="color"/>
<java-symbol name="system_theme_app_ring_light" type="color"/>
- <java-symbol name="system_on_theme_app_ring_light" type="color"/>
+ <java-symbol name="system_theme_notif_light" type="color"/>
<java-symbol name="system_brand_a_light" type="color"/>
<java-symbol name="system_brand_b_light" type="color"/>
<java-symbol name="system_brand_c_light" type="color"/>
@@ -5342,7 +5350,7 @@
<java-symbol name="system_theme_app_dark" type="color"/>
<java-symbol name="system_on_theme_app_dark" type="color"/>
<java-symbol name="system_theme_app_ring_dark" type="color"/>
- <java-symbol name="system_on_theme_app_ring_dark" type="color"/>
+ <java-symbol name="system_theme_notif_dark" type="color"/>
<java-symbol name="system_brand_a_dark" type="color"/>
<java-symbol name="system_brand_b_dark" type="color"/>
<java-symbol name="system_brand_c_dark" type="color"/>
@@ -5424,6 +5432,7 @@
<java-symbol type="drawable" name="focus_event_pressed_key_background" />
<java-symbol type="drawable" name="focus_event_rotary_input_background" />
<java-symbol type="string" name="config_defaultShutdownVibrationFile" />
+ <java-symbol type="bool" name="config_disableShutdownVibrationInZen" />
<java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
<java-symbol type="bool" name="config_enable_a11y_magnification_single_panning" />
@@ -5515,6 +5524,12 @@
<java-symbol type="string" name="biometric_dangling_notification_action_set_up" />
<java-symbol type="string" name="biometric_dangling_notification_action_not_now" />
+ <!-- Notification bundles -->
+ <java-symbol type="string" name="promotional_notification_channel_label"/>
+ <java-symbol type="string" name="social_notification_channel_label"/>
+ <java-symbol type="string" name="news_notification_channel_label"/>
+ <java-symbol type="string" name="recs_notification_channel_label"/>
+
<!-- Priority Modes icons -->
<java-symbol type="drawable" name="ic_zen_mode_type_bedtime" />
<java-symbol type="drawable" name="ic_zen_mode_type_driving" />
@@ -5526,4 +5541,9 @@
<java-symbol type="drawable" name="ic_zen_mode_type_theater" />
<java-symbol type="drawable" name="ic_zen_mode_type_unknown" />
+ <!-- System notification for background user sound -->
+ <java-symbol type="string" name="bg_user_sound_notification_title_alarm" />
+ <java-symbol type="string" name="bg_user_sound_notification_button_switch_user" />
+ <java-symbol type="string" name="bg_user_sound_notification_button_mute" />
+ <java-symbol type="string" name="bg_user_sound_notification_message" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 24d4938..382ff04 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -292,7 +292,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -410,7 +410,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -527,7 +527,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -646,7 +646,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -764,7 +764,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -890,7 +890,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1007,7 +1007,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1123,7 +1123,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1240,7 +1240,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1373,7 +1373,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1491,7 +1491,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1607,7 +1607,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1725,7 +1725,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1842,7 +1842,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -1959,7 +1959,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2076,7 +2076,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -2193,7 +2193,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -2310,7 +2310,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2432,7 +2432,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2547,7 +2547,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -2800,7 +2800,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -2917,7 +2917,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3033,7 +3033,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3150,7 +3150,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3269,7 +3269,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3387,7 +3387,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3511,7 +3511,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3631,7 +3631,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3750,7 +3750,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3870,7 +3870,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -3971,7 +3971,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4072,7 +4072,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4192,7 +4192,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4313,7 +4313,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4432,7 +4432,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4550,7 +4550,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4667,7 +4667,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4784,7 +4784,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -4899,7 +4899,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5022,7 +5022,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5126,7 +5126,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5222,7 +5222,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5339,7 +5339,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -5440,7 +5440,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5581,7 +5581,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5700,7 +5700,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5845,7 +5845,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -5920,7 +5920,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -5999,7 +5999,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
@@ -6074,7 +6074,7 @@
<item name="customColorThemeApp">@color/system_theme_app_light</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_light</item>
<item name="customColorBrandA">@color/system_brand_a_light</item>
<item name="customColorBrandB">@color/system_brand_b_light</item>
<item name="customColorBrandC">@color/system_brand_c_light</item>
@@ -6160,7 +6160,7 @@
<item name="customColorThemeApp">@color/system_theme_app_dark</item>
<item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
<item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
- <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorThemeNotif">@color/system_theme_notif_dark</item>
<item name="customColorBrandA">@color/system_brand_a_dark</item>
<item name="customColorBrandB">@color/system_brand_b_dark</item>
<item name="customColorBrandC">@color/system_brand_c_dark</item>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
index b3a0aba..db3a2da 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
@@ -470,6 +470,20 @@
}
@Test
+ public void equals_forMetadataWithDifferentContents_returnsFalse() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+ RadioMetadata metadata1 = mBuilder
+ .putStringArray(RadioMetadata.METADATA_KEY_UFIDS, UFIDS_VALUE)
+ .build();
+ RadioMetadata metadata2 = mBuilder
+ .putStringArray(RadioMetadata.METADATA_KEY_UFIDS, new String[]{"ufid3", "ufid2"})
+ .build();
+
+ mExpect.withMessage("Metadata with the same contents")
+ .that(metadata1).isNotEqualTo(metadata2);
+ }
+
+ @Test
public void describeContents_forMetadata() {
RadioMetadata metadata = mBuilder.build();
@@ -553,4 +567,25 @@
.that(metadata.getBitmap(RadioMetadata.METADATA_KEY_ICON))
.isEqualTo(bitmapResized);
}
+
+ @Test
+ public void toString_containsMetadataValues() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+ RadioMetadata metadataExpected = mBuilder
+ .putInt(RadioMetadata.METADATA_KEY_RDS_PI, INT_KEY_VALUE)
+ .putString(RadioMetadata.METADATA_KEY_ARTIST, ARTIST_KEY_VALUE)
+ .putStringArray(RadioMetadata.METADATA_KEY_UFIDS, UFIDS_VALUE)
+ .build();
+
+ String metadateString = metadataExpected.toString();
+
+ mExpect.withMessage("RDS PI value in converted sting for metadata")
+ .that(metadateString).contains(Integer.toString(INT_KEY_VALUE));
+ mExpect.withMessage("Artist value in converted sting for metadata")
+ .that(metadateString).contains(ARTIST_KEY_VALUE);
+ for (int i = 0; i < UFIDS_VALUE.length; i++) {
+ mExpect.withMessage("UFIDs[%s] value in converted sting for metadata", i)
+ .that(metadateString).contains(UFIDS_VALUE[i]);
+ }
+ }
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index f3d4ccf..3fb1554 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -15,6 +15,7 @@
*/
package com.android.server.broadcastradio.aidl;
+import android.annotation.Nullable;
import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.Metadata;
import android.hardware.broadcastradio.ProgramIdentifier;
@@ -110,20 +111,25 @@
static ProgramInfo makeHalProgramInfo(
android.hardware.broadcastradio.ProgramSelector hwSel,
- ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
- int hwSignalQuality) {
+ @Nullable ProgramIdentifier logicallyTunedTo,
+ @Nullable ProgramIdentifier physicallyTunedTo, int hwSignalQuality) {
return makeHalProgramInfo(hwSel, logicallyTunedTo, physicallyTunedTo, hwSignalQuality,
new ProgramIdentifier[]{}, new Metadata[]{});
}
static ProgramInfo makeHalProgramInfo(
android.hardware.broadcastradio.ProgramSelector hwSel,
- ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
- int hwSignalQuality, ProgramIdentifier[] relatedContent, Metadata[] metadata) {
+ @Nullable ProgramIdentifier logicallyTunedTo,
+ @Nullable ProgramIdentifier physicallyTunedTo, int hwSignalQuality,
+ ProgramIdentifier[] relatedContent, Metadata[] metadata) {
ProgramInfo hwInfo = new ProgramInfo();
hwInfo.selector = hwSel;
- hwInfo.logicallyTunedTo = logicallyTunedTo;
- hwInfo.physicallyTunedTo = physicallyTunedTo;
+ if (logicallyTunedTo != null) {
+ hwInfo.logicallyTunedTo = logicallyTunedTo;
+ }
+ if (physicallyTunedTo != null) {
+ hwInfo.physicallyTunedTo = physicallyTunedTo;
+ }
hwInfo.signalQuality = hwSignalQuality;
hwInfo.relatedContent = relatedContent;
hwInfo.metadata = metadata;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 86a6744..bdc4d25 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -255,6 +255,17 @@
}
@Test
+ public void throwOnError_withUnknownErrorCode() {
+ int invalidErrorCode = 100;
+ ServiceSpecificException halException = new ServiceSpecificException(invalidErrorCode);
+
+ RuntimeException thrown = ConversionUtils.throwOnError(halException, "seek");
+
+ expect.withMessage("Exception thrown for unknown error code")
+ .that(thrown).hasMessageThat().contains("seek: unknown error");
+ }
+
+ @Test
public void propertiesFromHalProperties_idsMatch() {
expect.withMessage("Properties id")
.that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID);
@@ -296,7 +307,7 @@
Map<String, Integer> dabTableExpected = Map.of(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1,
DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2);
- expect.withMessage("Supported program types")
+ expect.withMessage("DAB frequency table")
.that(MODULE_PROPERTIES.getDabFrequencyTable())
.containsExactlyEntriesIn(dabTableExpected);
}
@@ -551,13 +562,29 @@
@Test
public void programInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
- AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID,
- new ProgramIdentifier[]{TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+ AidlTestUtils.makeHalSelector(TEST_HAL_DAB_ENSEMBLE_ID,
+ new ProgramIdentifier[]{TEST_HAL_DAB_FREQUENCY_ID});
+ ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(invalidHalDabSelector,
+ /* logicallyTunedTo= */ null, /* physicallyTunedTo= */ null,
+ TEST_SIGNAL_QUALITY);
+
+ RadioManager.ProgramInfo programInfo =
+ ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+
+ expect.withMessage("Invalid DAB program info with incorrect type of physically tuned to id")
+ .that(programInfo).isNull();
+ }
+
+ @Test
+ public void tunedProgramInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
+ android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
+ TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(invalidHalDabSelector,
TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
RadioManager.ProgramInfo programInfo =
- ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+ ConversionUtils.tunedProgramInfoFromHalProgramInfo(halProgramInfo);
expect.withMessage("Invalid DAB program info with incorrect type of physically tuned to id")
.that(programInfo).isNull();
@@ -621,7 +648,7 @@
@Test
public void programInfoMeetsSdkVersionRequirement_withLowerVersionIdForRelatedContent() {
RadioManager.ProgramInfo dabProgramInfo = new RadioManager.ProgramInfo(
- TEST_DAB_SELECTOR_LEGACY, TEST_DAB_SID_EXT_ID, TEST_DAB_FREQUENCY_ID,
+ TEST_DAB_SELECTOR_LEGACY, TEST_DAB_SID_EXT_LEGACY_ID, TEST_DAB_FREQUENCY_ID,
List.of(TEST_DAB_SID_EXT_ID), /* infoFlags= */ 0, TEST_SIGNAL_QUALITY,
new RadioMetadata.Builder().build(), new ArrayMap<>());
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
index d64fcaf..b1856719 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
@@ -189,13 +189,16 @@
@Test
public void updateFromHalProgramListChunk_withInvalidChunk() {
- RadioManager.ProgramInfo invalidDabInfo = AidlTestUtils.makeProgramInfo(TEST_DAB_SELECTOR,
- TEST_DAB_DMB_SID_EXT_ID, TEST_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
+ ProgramInfo invalidHalDabInfo = AidlTestUtils.makeHalProgramInfo(
+ AidlTestUtils.makeHalSelector(
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_ENSEMBLE_ID),
+ new ProgramIdentifier[]{}), /* logicallyTunedTo= */ null,
+ /* physicallyTunedTo= */ null, TEST_SIGNAL_QUALITY);
ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
/* complete= */ false);
ProgramListChunk chunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
- /* complete= */ true, new ProgramInfo[]{AidlTestUtils.programInfoToHalProgramInfo(
- invalidDabInfo)}, new ProgramIdentifier[]{});
+ /* complete= */ true, new ProgramInfo[]{invalidHalDabInfo},
+ new ProgramIdentifier[]{});
cache.updateFromHalProgramListChunk(chunk);
@@ -447,10 +450,10 @@
/* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1];
halModified[0] = AidlTestUtils.makeHalProgramInfo(
- ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE),
- ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
- ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
- TEST_SIGNAL_QUALITY);
+ AidlTestUtils.makeHalSelector(
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_ENSEMBLE_ID),
+ new ProgramIdentifier[]{}), /* logicallyTunedTo= */ null,
+ /* physicallyTunedTo= */ null, TEST_SIGNAL_QUALITY);
ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1];
halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier();
ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
index 98641ef..c5c3a61 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
@@ -32,6 +32,7 @@
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
+import android.os.ParcelableException;
import android.util.ArrayMap;
import com.google.common.truth.Expect;
@@ -89,6 +90,15 @@
}
@Test
+ public void throwOnError_withUnknownErrorCode() {
+ ParcelableException thrown = assertThrows(ParcelableException.class, () ->
+ Convert.throwOnError("tune", /* result= */ 1000));
+
+ expect.withMessage("Exception for unknown error code").that(thrown)
+ .hasMessageThat().contains("unknown error");
+ }
+
+ @Test
public void propertiesFromHalProperties_idsMatch() {
expect.withMessage("Properties id")
.that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID);
@@ -232,6 +242,12 @@
}
@Test
+ public void vendorInfoFromHalVendorKeyValues_withNull() {
+ expect.withMessage("Null vendor info converted from HAL")
+ .that(Convert.vendorInfoFromHal(/* info= */ null)).isEmpty();
+ }
+
+ @Test
public void vendorInfoFromHalVendorKeyValues_withNullElements() {
VendorKeyValue halVendorInfo = new VendorKeyValue();
halVendorInfo.key = null;
diff --git a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
index 12a42f9..ed5e4af 100644
--- a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -37,15 +37,20 @@
long myChangeId = 500L, otherChangeId = 600L;
int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertTrue(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(myUid, myChangeId, myState)));
reporter.reportChange(myUid, myChangeId, myState);
// Same report will not be logged again.
- assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(myUid, myChangeId, myState)));
// Other reports will be logged.
- assertTrue(reporter.shouldWriteToStatsLog(otherUid, myChangeId, myState));
- assertTrue(reporter.shouldWriteToStatsLog(myUid, otherChangeId, myState));
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, otherState));
+ assertTrue(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(otherUid, myChangeId, myState)));
+ assertTrue(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(myUid, otherChangeId, myState)));
+ assertTrue(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(myUid, myChangeId, otherState)));
}
@Test
@@ -55,15 +60,18 @@
long myChangeId = 500L;
int myState = ChangeReporter.STATE_ENABLED;
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertTrue(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(myUid, myChangeId, myState)));
reporter.reportChange(myUid, myChangeId, myState);
// Same report will not be logged again.
- assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(myUid, myChangeId, myState)));
reporter.resetReportedChanges(myUid);
// Same report will be logged again after reset.
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertTrue(reporter.shouldWriteToStatsLog(false,
+ reporter.isAlreadyReported(myUid, myChangeId, myState)));
}
@Test
@@ -196,4 +204,21 @@
// off.
assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myDisabledState, false));
}
+
+ @Test
+ public void testDontLogSystemApps() {
+ // Verify we don't log an app if we know it's a system app when source is system server.
+ ChangeReporter systemServerReporter =
+ new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
+
+ assertFalse(systemServerReporter.shouldWriteToStatsLog(true, false));
+ assertFalse(systemServerReporter.shouldWriteToStatsLog(true, true));
+
+ // Verify we don't log an app if we know it's a system app when source is unknown.
+ ChangeReporter unknownReporter =
+ new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
+
+ assertFalse(unknownReporter.shouldWriteToStatsLog(true, false));
+ assertFalse(unknownReporter.shouldWriteToStatsLog(true, true));
+ }
}
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
index 7c1ac48..15e07e5 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -30,6 +30,7 @@
"android.test.base",
],
static_libs: [
+ "android.tracing.flags-aconfig-java",
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
"truth",
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 8072d69..7294d4c 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -71,6 +71,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -102,17 +103,11 @@
// associated with the bugreport).
private static final String INTENT_BUGREPORT_FINISHED =
"com.android.internal.intent.action.BUGREPORT_FINISHED";
- private static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
- private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
- private static final Path[] UI_TRACES_PREDUMPED = {
+ private ArrayList<Path> mUiTracesPreDumped = new ArrayList<>(Arrays.asList(
Paths.get("/data/misc/perfetto-traces/bugreport/systrace.pftrace"),
- Paths.get("/data/misc/wmtrace/ime_trace_clients.winscope"),
- Paths.get("/data/misc/wmtrace/ime_trace_managerservice.winscope"),
- Paths.get("/data/misc/wmtrace/ime_trace_service.winscope"),
- Paths.get("/data/misc/wmtrace/wm_trace.winscope"),
- Paths.get("/data/misc/wmtrace/wm_log.winscope"),
- };
+ Paths.get("/data/misc/wmtrace/wm_trace.winscope")
+ ));
private Handler mHandler;
private Executor mExecutor;
@@ -124,6 +119,17 @@
@Before
public void setup() throws Exception {
+ if (!android.tracing.Flags.perfettoIme()) {
+ mUiTracesPreDumped.add(Paths.get("/data/misc/wmtrace/ime_trace_clients.winscope"));
+ mUiTracesPreDumped.add(
+ Paths.get("/data/misc/wmtrace/ime_trace_managerservice.winscope"));
+ mUiTracesPreDumped.add(Paths.get("/data/misc/wmtrace/ime_trace_service.winscope"));
+ }
+
+ if (!android.tracing.Flags.perfettoProtologTracing()) {
+ mUiTracesPreDumped.add(Paths.get("/data/misc/wmtrace/wm_log.winscope"));
+ }
+
mHandler = createHandler();
mExecutor = (runnable) -> {
if (mHandler != null) {
@@ -206,7 +212,7 @@
mBrm.preDumpUiData();
waitTillDumpstateExitedOrTimeout();
- List<File> expectedPreDumpedTraceFiles = copyFiles(UI_TRACES_PREDUMPED);
+ List<File> expectedPreDumpedTraceFiles = copyFiles(mUiTracesPreDumped);
BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, null, fullWithUsePreDumpFlag(), mExecutor,
@@ -220,9 +226,9 @@
assertThat(mBugreportFile.length()).isGreaterThan(0L);
assertFdsAreClosed(mBugreportFd);
- assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
+ assertThatBugreportContainsFiles(mUiTracesPreDumped);
- List<File> actualPreDumpedTraceFiles = extractFilesFromBugreport(UI_TRACES_PREDUMPED);
+ List<File> actualPreDumpedTraceFiles = extractFilesFromBugreport(mUiTracesPreDumped);
assertThatAllFileContentsAreEqual(actualPreDumpedTraceFiles, expectedPreDumpedTraceFiles);
}
@@ -235,9 +241,9 @@
// In some corner cases, data dumped as part of the full bugreport could be the same as the
// pre-dumped data and this test would fail. Hence, here we create fake/artificial
// pre-dumped data that we know it won't match with the full bugreport data.
- createFakeTraceFiles(UI_TRACES_PREDUMPED);
+ createFakeTraceFiles(mUiTracesPreDumped);
- List<File> preDumpedTraceFiles = copyFiles(UI_TRACES_PREDUMPED);
+ List<File> preDumpedTraceFiles = copyFiles(mUiTracesPreDumped);
BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, null, full(), mExecutor,
@@ -251,9 +257,9 @@
assertThat(mBugreportFile.length()).isGreaterThan(0L);
assertFdsAreClosed(mBugreportFd);
- assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
+ assertThatBugreportContainsFiles(mUiTracesPreDumped);
- List<File> actualTraceFiles = extractFilesFromBugreport(UI_TRACES_PREDUMPED);
+ List<File> actualTraceFiles = extractFilesFromBugreport(mUiTracesPreDumped);
assertThatAllFileContentsAreDifferent(preDumpedTraceFiles, actualTraceFiles);
}
@@ -270,7 +276,7 @@
// 1. Pre-dump data
// 2. Start bugreport + "use pre-dump" flag (USE AND REMOVE THE PRE-DUMP FROM DISK)
// 3. Start bugreport + "use pre-dump" flag (NO PRE-DUMP AVAILABLE ON DISK)
- removeFilesIfNeeded(UI_TRACES_PREDUMPED);
+ removeFilesIfNeeded(mUiTracesPreDumped);
// Start bugreport with "use predump" flag. Because the pre-dumped data is not available
// the flag will be ignored and data will be dumped as in normal flow.
@@ -286,7 +292,7 @@
assertThat(mBugreportFile.length()).isGreaterThan(0L);
assertFdsAreClosed(mBugreportFd);
- assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
+ assertThatBugreportContainsFiles(mUiTracesPreDumped);
}
@Test
@@ -555,7 +561,7 @@
);
}
- private void assertThatBugreportContainsFiles(Path[] paths)
+ private void assertThatBugreportContainsFiles(List<Path> paths)
throws IOException {
List<Path> entries = listZipArchiveEntries(mBugreportFile);
for (Path pathInDevice : paths) {
@@ -564,7 +570,7 @@
}
}
- private List<File> extractFilesFromBugreport(Path[] paths) throws Exception {
+ private List<File> extractFilesFromBugreport(List<Path> paths) throws Exception {
List<File> files = new ArrayList<File>();
for (Path pathInDevice : paths) {
Path pathInArchive = Paths.get("FS" + pathInDevice.toString());
@@ -614,7 +620,7 @@
return extractedFile;
}
- private static void createFakeTraceFiles(Path[] paths) throws Exception {
+ private static void createFakeTraceFiles(List<Path> paths) throws Exception {
File src = createTempFile("fake", ".data");
Files.write("fake data".getBytes(StandardCharsets.UTF_8), src);
@@ -631,7 +637,7 @@
);
}
- private static List<File> copyFiles(Path[] paths) throws Exception {
+ private static List<File> copyFiles(List<Path> paths) throws Exception {
ArrayList<File> files = new ArrayList<File>();
for (Path src : paths) {
File dst = createTempFile(src.getFileName().toString(), ".copy");
@@ -643,7 +649,7 @@
return files;
}
- private static void removeFilesIfNeeded(Path[] paths) throws Exception {
+ private static void removeFilesIfNeeded(List<Path> paths) throws Exception {
for (Path path : paths) {
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
"rm -f " + path.toString()
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 41696df..d277169 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -63,6 +63,7 @@
"-c fa",
],
static_libs: [
+ "A11yChecker",
"collector-device-lib-platform",
"frameworks-base-testutils",
"core-test-rules", // for libcore.dalvik.system.CloseGuardSupport
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index b64eeca..e9ad1c2 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -804,6 +804,7 @@
}
@Test
+ @Ignore // b/347089000 - Restore or delete
public void testColors_ensureColors_colorized_producesValidPalette_white() {
validateColorizedPaletteForColor(Color.WHITE);
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index ee1d1e1..0b270d4 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -20,9 +20,6 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
-import static com.android.window.flags.Flags.FLAG_WINDOW_TOKEN_CONFIG_THREAD_SAFE;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -105,8 +102,6 @@
@Before
public void setup() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
MockitoAnnotations.initMocks(this);
mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
mHandler = getInstrumentation().getContext().getMainThreadHandler();
@@ -192,8 +187,6 @@
@Test
public void testWindowTokenClient_onConfigurationChanged() {
- mSetFlagsRule.enableFlags(FLAG_WINDOW_TOKEN_CONFIG_THREAD_SAFE);
-
doNothing().when(mController).onContextConfigurationPreChanged(any());
doNothing().when(mController).onContextConfigurationPostChanged(any());
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index 5272416..79659ca 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -16,22 +16,16 @@
package android.app.servertransaction;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
-
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.ClientTransactionHandler;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,24 +43,8 @@
@Presubmit
public class ClientTransactionTests {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
@Test
public void testPreExecute() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testPreExecuteInner();
- }
-
- @Test
- public void testPreExecute_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testPreExecuteInner();
- }
-
- private void testPreExecuteInner() {
final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 935bc75..73b7447 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -25,9 +25,6 @@
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -54,14 +51,12 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -88,9 +83,6 @@
@Presubmit
public class TransactionExecutorTests {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
@Mock
private ClientTransactionHandler mTransactionHandler;
@Mock
@@ -248,19 +240,6 @@
@Test
public void testTransactionResolution() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testTransactionResolutionInner();
- }
-
- @Test
- public void testTransactionResolution_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testTransactionResolutionInner();
- }
-
- private void testTransactionResolutionInner() {
ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
@@ -284,19 +263,6 @@
@Test
public void testDoNotLaunchDestroyedActivity() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testDoNotLaunchDestroyedActivityInner();
- }
-
- @Test
- public void testDoNotLaunchDestroyedActivity_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testDoNotLaunchDestroyedActivityInner();
- }
-
- private void testDoNotLaunchDestroyedActivityInner() {
final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
// Assume launch transaction is still in queue, so there is no client record.
@@ -329,19 +295,6 @@
@Test
public void testActivityResultRequiredStateResolution() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityResultRequiredStateResolutionInner();
- }
-
- @Test
- public void testActivityResultRequiredStateResolution_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityResultRequiredStateResolutionInner();
- }
-
- private void testActivityResultRequiredStateResolutionInner() {
when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
PostExecItem postExecItem = new PostExecItem(ON_RESUME);
@@ -495,19 +448,6 @@
@Test(expected = IllegalArgumentException.class)
public void testActivityItemNullRecordThrowsException() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemNullRecordThrowsExceptionInner();
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testActivityItemNullRecordThrowsException_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemNullRecordThrowsExceptionInner();
- }
-
- private void testActivityItemNullRecordThrowsExceptionInner() {
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
final IBinder token = mock(IBinder.class);
@@ -520,19 +460,6 @@
@Test
public void testActivityItemExecute() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemExecuteInner();
- }
-
- @Test
- public void testActivityItemExecute_bundleClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- testActivityItemExecuteInner();
- }
-
- private void testActivityItemExecuteInner() {
final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index d451fe5..a4d7661 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -20,9 +20,6 @@
import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertEquals;
@@ -40,14 +37,12 @@
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.window.ActivityWindowInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,9 +62,6 @@
@Presubmit
public class TransactionParcelTests {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
private Parcel mParcel;
private IBinder mActivityToken;
@@ -296,8 +288,6 @@
@Test
public void testClientTransaction() {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
@@ -320,49 +310,6 @@
assertEquals(mActivityToken, result.getActivityToken());
}
- @Test
- public void testClientTransactionCallbacksOnly() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Write to parcel
- NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
- ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config(), new ActivityWindowInfo());
-
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addTransactionItem(callback1);
- transaction.addTransactionItem(callback2);
-
- writeAndPrepareForReading(transaction);
-
- // Read from parcel and assert
- ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
-
- assertEquals(transaction.hashCode(), result.hashCode());
- assertEquals(transaction, result);
- assertEquals(mActivityToken, result.getActivityToken());
- }
-
- @Test
- public void testClientTransactionLifecycleOnly() {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Write to parcel
- StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken);
-
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addTransactionItem(lifecycleRequest);
-
- writeAndPrepareForReading(transaction);
-
- // Read from parcel and assert
- ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel);
-
- assertEquals(transaction.hashCode(), result.hashCode());
- assertEquals(transaction, result);
- assertEquals(mActivityToken, result.getActivityToken());
- }
-
/** Write to {@link #mParcel} and reset its position to prepare for reading from the start. */
private void writeAndPrepareForReading(Parcelable parcelable) {
parcelable.writeToParcel(mParcel, 0 /* flags */);
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 7b70b41..c8015d4 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -87,7 +87,7 @@
bitmap.compress(Bitmap.CompressFormat.PNG, 90, mImage.getOutputStream());
final AssetFileDescriptor afd = new AssetFileDescriptor(
- new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
+ ParcelFileDescriptor.dup(mImage.getFileDescriptor()), 0, mSize, null);
when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(
afd);
}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 6b3cf7b..ee1b658 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -44,7 +44,6 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -382,8 +381,7 @@
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
@@ -414,9 +412,7 @@
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
-
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
}
@@ -452,9 +448,7 @@
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
-
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
}
@@ -493,9 +487,7 @@
assertTrue(allResourcePathsLoaded(resourcePaths, loadedAssets));
// Package resources' paths should be cached in ResourcesManager.
- assertEquals(Arrays.toString(resourcePaths), Arrays.toString(ResourcesManager.getInstance()
- .getSharedLibAssetsMap().get(TEST_LIB).getAllAssetPaths()));
-
+ assertNotNull(ResourcesManager.getInstance().getRegisteredResourcePaths().get(TEST_LIB));
// Revert the ResourcesManager instance back.
ResourcesManager.setInstance(oriResourcesManager);
}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java
new file mode 100644
index 0000000..09c380a
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCantOpenDatabaseExceptionTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.OpenParams;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SQLiteCantOpenDatabaseExceptionTest {
+ private static final String TAG = "SQLiteCantOpenDatabaseExceptionTest";
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ private File getDatabaseFile(String name) {
+ mContext.deleteDatabase(name);
+
+ // getDatabasePath() doesn't like a filename with a / in it, so we can't use it directly.
+ return new File(mContext.getDatabasePath("a").getParentFile(), name);
+ }
+
+ private void callWithExpectedMessage(File file, String expectedMessagePattern) {
+ try {
+ SQLiteDatabase.openDatabase(file, new OpenParams.Builder().build());
+ fail("SQLiteCantOpenDatabaseException was not thrown");
+ } catch (SQLiteCantOpenDatabaseException e) {
+ Log.i(TAG, "Caught expected exception: " + e.getMessage());
+ assertTrue(e.getMessage().matches(expectedMessagePattern));
+ }
+ }
+
+ /** DB's directory doesn't exist. */
+ @Test
+ public void testDirectoryDoesNotExist() {
+ final File file = getDatabaseFile("nonexisitentdir/mydb.db");
+
+ callWithExpectedMessage(file, ".*: Directory .* doesn't exist");
+ }
+
+ /** File doesn't exist */
+ @Test
+ public void testFileDoesNotExist() {
+ final File file = getDatabaseFile("mydb.db");
+
+ callWithExpectedMessage(file, ".*: File .* doesn't exist");
+ }
+
+ /** File exists, but not readable. */
+ @Test
+ public void testFileNotReadable() {
+ final File file = getDatabaseFile("mydb.db");
+
+ SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(file, null);
+ db.close();
+
+ file.setReadable(false);
+
+ callWithExpectedMessage(file, ".*: File .* not readable");
+ }
+
+ /** Directory with the given name exists already. */
+ @Test
+ public void testPathIsADirectory() {
+ final File file = getDatabaseFile("mydb.db");
+
+ file.mkdirs();
+
+ callWithExpectedMessage(file, ".*: Path .* is a directory");
+ }
+}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 8071d3d..24d27c4 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -27,7 +27,6 @@
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.os.SystemClock;
-import android.test.AndroidTestCase;
import android.util.Log;
import androidx.test.filters.SmallTest;
diff --git a/core/tests/coretests/src/android/graphics/ColorStateListTest.java b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
index a3d52ea..ab41bd0 100644
--- a/core/tests/coretests/src/android/graphics/ColorStateListTest.java
+++ b/core/tests/coretests/src/android/graphics/ColorStateListTest.java
@@ -19,6 +19,8 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.test.AndroidTestCase;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
import androidx.test.filters.SmallTest;
@@ -49,6 +51,15 @@
}
@SmallTest
+ public void testStateIsInList_proto() throws Exception {
+ ColorStateList colorStateList = recreateFromProto(
+ mResources.getColorStateList(R.color.color1));
+ int[] focusedState = {android.R.attr.state_focused};
+ int focusColor = colorStateList.getColorForState(focusedState, R.color.failColor);
+ assertEquals(mResources.getColor(R.color.testcolor1), focusColor);
+ }
+
+ @SmallTest
public void testEmptyState() throws Exception {
ColorStateList colorStateList = mResources.getColorStateList(R.color.color1);
int[] emptyState = {};
@@ -57,6 +68,15 @@
}
@SmallTest
+ public void testEmptyState_proto() throws Exception {
+ ColorStateList colorStateList = recreateFromProto(
+ mResources.getColorStateList(R.color.color1));
+ int[] emptyState = {};
+ int defaultColor = colorStateList.getColorForState(emptyState, mFailureColor);
+ assertEquals(mResources.getColor(R.color.testcolor2), defaultColor);
+ }
+
+ @SmallTest
public void testGetColor() throws Exception {
int defaultColor = mResources.getColor(R.color.color1);
assertEquals(mResources.getColor(R.color.testcolor2), defaultColor);
@@ -73,4 +93,11 @@
int defaultColor = mResources.getColor(R.color.color_with_lstar);
assertEquals(mResources.getColor(R.color.testcolor3), defaultColor);
}
+
+ private ColorStateList recreateFromProto(ColorStateList colorStateList) throws Exception {
+ ProtoOutputStream out = new ProtoOutputStream();
+ colorStateList.writeToProto(out);
+ ProtoInputStream in = new ProtoInputStream(out.getBytes());
+ return ColorStateList.createFromProto(in);
+ }
}
diff --git a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
index ca91542..5464ea3 100644
--- a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
+++ b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -209,6 +210,25 @@
"The number of list items exceeds ");
}
+ @Test
+ public void testOnDialogDismissed_dialogDismissedNegative() throws RemoteException {
+ final ArgumentCaptor<IBiometricServiceReceiver> biometricServiceReceiverCaptor =
+ ArgumentCaptor.forClass(IBiometricServiceReceiver.class);
+ final BiometricPrompt.AuthenticationCallback callback =
+ mock(BiometricPrompt.AuthenticationCallback.class);
+ mBiometricPrompt.authenticate(mCancellationSignal, mExecutor, callback);
+ mLooper.dispatchAll();
+
+ verify(mService).authenticate(any(), anyLong(), anyInt(),
+ biometricServiceReceiverCaptor.capture(), anyString(), any());
+
+ biometricServiceReceiverCaptor.getValue().onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+
+ verify(callback).onAuthenticationError(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+ null /* errString */);
+ }
+
private String generateRandomString(int charNum) {
final Random random = new Random();
final StringBuilder longString = new StringBuilder(charNum);
diff --git a/core/tests/coretests/src/android/net/OWNERS b/core/tests/coretests/src/android/net/OWNERS
index beb77dc..831f444 100644
--- a/core/tests/coretests/src/android/net/OWNERS
+++ b/core/tests/coretests/src/android/net/OWNERS
@@ -1,5 +1,5 @@
include /services/core/java/com/android/server/net/OWNERS
-per-file SSL*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
+per-file SSL*,Url* = file:platform/libcore:main:/OWNERS_net_tests
per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS
per-file Uri* = varunshah@google.com
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 851e612..8cd6773 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -16,7 +16,6 @@
package android.os;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.MediumTest;
@@ -154,7 +153,6 @@
@Test
@MediumTest
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testFieldIntegrity() throws Exception {
TestHandlerThread tester = new TestFieldIntegrityHandler() {
diff --git a/core/tests/coretests/src/android/util/SequenceUtilsTest.java b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
new file mode 100644
index 0000000..020520d
--- /dev/null
+++ b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+
+import static android.util.SequenceUtils.getInitSeq;
+import static android.util.SequenceUtils.getNextSeq;
+import static android.util.SequenceUtils.isIncomingSeqNewer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for subtypes of {@link SequenceUtils}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:SequenceUtilsTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+@DisabledOnRavenwood(blockedBy = SequenceUtils.class)
+public class SequenceUtilsTest {
+
+ // This is needed to disable the test in Ravenwood test, because SequenceUtils hasn't opted in
+ // for Ravenwood, which is still in experiment.
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Test
+ public void testNextSeq() {
+ assertEquals(getInitSeq() + 1, getNextSeq(getInitSeq()));
+ assertEquals(getInitSeq() + 1, getNextSeq(Integer.MAX_VALUE));
+ }
+
+ @Test
+ public void testIsIncomingSeqNewer() {
+ assertTrue(isIncomingSeqNewer(getInitSeq() + 1, getInitSeq() + 10));
+ assertFalse(isIncomingSeqNewer(getInitSeq() + 10, getInitSeq() + 1));
+ assertTrue(isIncomingSeqNewer(-100, 100));
+ assertFalse(isIncomingSeqNewer(100, -100));
+ assertTrue(isIncomingSeqNewer(1, 2));
+ assertFalse(isIncomingSeqNewer(2, 1));
+
+ // Possible incoming seq are all newer than the initial seq.
+ assertTrue(isIncomingSeqNewer(getInitSeq(), getInitSeq() + 1));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), -100));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), 0));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), 100));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), Integer.MAX_VALUE));
+ assertTrue(isIncomingSeqNewer(getInitSeq(), getNextSeq(Integer.MAX_VALUE)));
+
+ // False for the same seq.
+ assertFalse(isIncomingSeqNewer(getInitSeq(), getInitSeq()));
+ assertFalse(isIncomingSeqNewer(100, 100));
+ assertFalse(isIncomingSeqNewer(Integer.MAX_VALUE, Integer.MAX_VALUE));
+
+ // True when there is a large jump (overflow).
+ assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 1));
+ assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 100));
+ assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getNextSeq(Integer.MAX_VALUE)));
+ }
+}
diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
index 1c72185..a8dce70 100644
--- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
@@ -17,7 +17,6 @@
import static com.google.common.truth.Truth.assertThat;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
@@ -37,7 +36,6 @@
public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testAddAll() {
final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>();
@@ -59,7 +57,6 @@
}
@Test
- @IgnoreUnderRavenwood(reason = "b/315036461")
public void testCopyConstructor() {
final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>();
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 169300a..c01b51d 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -21,6 +21,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
@@ -121,6 +122,52 @@
waitForAfterDraw();
}
+ @Test
+ @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
+ public void inputMethodWithContentMoves() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // update the window type to TYPE_INPUT_METHOD
+ int windowType = mViewRoot.mWindowAttributes.type;
+ final WindowManager.LayoutParams attrs = mViewRoot.mWindowAttributes;
+ attrs.type = TYPE_INPUT_METHOD;
+ instrumentation.runOnMainSync(() -> {
+ mViewRoot.setLayoutParams(attrs, false);
+ });
+ instrumentation.waitForIdleSync();
+
+ final WindowManager.LayoutParams newAttrs = mViewRoot.mWindowAttributes;
+ assertTrue(newAttrs.type == TYPE_INPUT_METHOD);
+
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.offsetLeftAndRight(100);
+ runAfterDraw(() -> {
+ if (toolkitFrameRateVelocityMappingReadOnly()) {
+ float frameRate = mViewRoot.getLastPreferredFrameRate();
+ // frame rate shouldn't be boost with TYPE_INPUT_METHOD window type
+ assertTrue(frameRate == 0);
+ } else {
+ assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+ });
+ });
+ waitForAfterDraw();
+
+ // Reset the window type back to the original one.
+ newAttrs.type = windowType;
+ instrumentation.runOnMainSync(() -> {
+ mViewRoot.setLayoutParams(newAttrs, false);
+ });
+ instrumentation.waitForIdleSync();
+ assertTrue(mViewRoot.mWindowAttributes.type == windowType);
+ }
+
@UiThreadTest
@Test
@RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
@@ -128,6 +175,87 @@
assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f);
}
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
+ public void highHintWhenActionMove() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
+
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
+ layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ mMovingView.setLayoutParams(layoutParams);
+ mMovingView.setOnClickListener((v) -> {});
+ });
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory()));
+
+ int[] position = new int[2];
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.getLocationOnScreen(position);
+ position[0] += mMovingView.getWidth() / 2;
+ position[1] += mMovingView.getHeight() / 2;
+ });
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ long now = SystemClock.uptimeMillis();
+ MotionEvent down = MotionEvent.obtain(
+ now, // downTime
+ now, // eventTime
+ MotionEvent.ACTION_DOWN, // action
+ position[0], // x
+ position[1], // y
+ 0 // metaState
+ );
+ down.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ instrumentation.sendPointerSync(down);
+
+ now = SystemClock.uptimeMillis();
+ MotionEvent move = MotionEvent.obtain(
+ now, // downTime
+ now, // eventTime
+ MotionEvent.ACTION_MOVE, // action
+ position[0], // x
+ position[1], // y
+ 0 // metaState
+ );
+ move.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ instrumentation.sendPointerSync(move);
+
+ // We should continue to enable touch boost even when GTE compatibility is present.
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.offsetLeftAndRight(10);
+ assertTrue(mViewRoot.getIsTouchBoosting());
+ });
+
+ now = SystemClock.uptimeMillis();
+ MotionEvent up = MotionEvent.obtain(
+ now, // downTime
+ now, // eventTime
+ MotionEvent.ACTION_UP, // action
+ position[0], // x
+ position[1], // y
+ 0 // metaState
+ );
+ up.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ instrumentation.sendPointerSync(up);
+
+ // No touch boost when there is no ongoing pressed gesture.
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.offsetLeftAndRight(10);
+ assertFalse(mViewRoot.getIsTouchBoosting());
+ });
+
+ down.recycle();
+ move.recycle();
+ up.recycle();
+ }
+
@Test
@RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
public void frameBoostDisable() throws Throwable {
@@ -161,7 +289,7 @@
@RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
- public void lowVelocity60() throws Throwable {
+ public void lowVelocity80() throws Throwable {
if (!ViewProperties.vrr_enabled().orElse(true)) {
return;
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 06cb0ee..9337bf6 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -16,13 +16,6 @@
package android.view;
-import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
-import static android.view.flags.Flags.FLAG_ADD_SCHANDLE_TO_VRI_SURFACE;
-import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
-import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY;
-import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY;
-import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
-import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
@@ -44,6 +37,13 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
+import static android.view.flags.Flags.FLAG_ADD_SCHANDLE_TO_VRI_SURFACE;
+import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY;
+import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY;
+import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY;
+import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
+import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly;
@@ -53,6 +53,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -63,9 +64,11 @@
import android.app.UiModeManager;
import android.content.Context;
import android.graphics.ForceDarkType;
+import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.SystemProperties;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -1250,6 +1253,81 @@
});
}
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY})
+ public void votePreferredFrameRate_infrequentLayer_smallView_voteForLow() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
+ final long delay = 200L;
+
+ mView = new View(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+ wmlp.width = 1;
+ wmlp.height = 1;
+
+ // The view is a small view, and it should vote for category low only.
+ int expected = FRAME_RATE_CATEGORY_LOW;
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ wm.addView(mView, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ mViewRootImpl = mView.getViewRootImpl();
+ waitForFrameRateCategoryToSettle(mView);
+
+ // In transition from frequent update to infrequent update
+ Thread.sleep(delay);
+ sInstrumentation.runOnMainSync(() -> {
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+
+ // In transition from frequent update to infrequent update
+ Thread.sleep(delay);
+ sInstrumentation.runOnMainSync(() -> {
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+
+ // Infrequent update
+ Thread.sleep(delay);
+
+ // The view is small, the expected category is still low for intermittent.
+ int intermittentExpected = FRAME_RATE_CATEGORY_LOW;
+
+ sInstrumentation.runOnMainSync(() -> {
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(intermittentExpected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+
+ // When the View vote, it's still considered as intermittent update state
+ sInstrumentation.runOnMainSync(() -> {
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(intermittentExpected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+
+ // Becomes frequent update state
+ sInstrumentation.runOnMainSync(() -> {
+ mView.invalidate();
+ runAfterDraw(() -> assertEquals(expected,
+ mViewRootImpl.getLastPreferredFrameRateCategory()));
+ });
+ }
+
/**
* Test the IsFrameRatePowerSavingsBalanced values are properly set
*/
@@ -1465,6 +1543,37 @@
nativeCreateASurfaceControlFromSurface(mViewRootImpl.mSurface));
}
+ @EnableFlags(Flags.FLAG_INSETS_CONTROL_SEQ)
+ @Test
+ public void testHandleInsetsControlChanged() {
+ mView = new View(sContext);
+ attachViewToWindow(mView);
+
+ mViewRootImpl = mView.getViewRootImpl();
+ final InsetsController controller = mViewRootImpl.getInsetsController();
+
+ final InsetsState state0 = new InsetsState();
+ final InsetsState state1 = new InsetsState();
+ state0.setDisplayFrame(new Rect(0, 0, 500, 1000));
+ state0.setSeq(10000);
+ state1.setDisplayFrame(new Rect(0, 0, 1500, 2000));
+ state1.setSeq(10001);
+ final InsetsSourceControl.Array array = new InsetsSourceControl.Array();
+
+ sInstrumentation.runOnMainSync(() -> {
+ mViewRootImpl.handleInsetsControlChanged(state0, array);
+ assertEquals(state0, controller.getLastDispatchedState());
+
+ mViewRootImpl.handleInsetsControlChanged(state1, array);
+ assertEquals(state1, controller.getLastDispatchedState());
+
+ // Skip the stale value.
+ mViewRootImpl.handleInsetsControlChanged(state0, array);
+ assertEquals(state1, controller.getLastDispatchedState());
+ assertNotEquals(state0, controller.getLastDispatchedState());
+ });
+ }
+
private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
try {
SystemProperties.set(
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 1013bf5..ce36ee0 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -20,6 +20,7 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_TILE_COMPONENT_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.google.common.truth.Truth.assertThat;
@@ -31,6 +32,8 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doThrow;
@@ -48,12 +51,14 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.view.Display;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.util.IntPair;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -321,7 +326,7 @@
Throwable rethrownException = assertThrows(RuntimeException.class,
() -> manager.enableShortcutsForTargets(
/* enable= */ false,
- UserShortcutType.HARDWARE,
+ HARDWARE,
Set.of(DALTONIZER_COMPONENT_NAME.flattenToString()),
UserHandle.USER_CURRENT
));
@@ -331,7 +336,7 @@
@Test
public void enableShortcutsForTargets_verifyServiceMethodCalled() throws Exception {
AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
- int shortcutTypes = UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP;
+ int shortcutTypes = HARDWARE | UserShortcutType.TRIPLETAP;
manager.enableShortcutsForTargets(
/* enable= */ false,
@@ -348,6 +353,30 @@
);
}
+ @Test
+ public void performAccessibilityShortcut_callToService_defaultTypeIsHardware()
+ throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+
+ manager.performAccessibilityShortcut();
+
+ verify(mMockService).performAccessibilityShortcut(
+ eq(Display.DEFAULT_DISPLAY), eq(HARDWARE), isNull());
+ }
+
+ @Test
+ public void performAccessibilityShortcut_callToService_typeParameterMatches() throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+ int display = Display.DEFAULT_DISPLAY;
+ String name = LABEL;
+
+ for (int type: ShortcutConstants.USER_SHORTCUT_TYPES) {
+ manager.performAccessibilityShortcut(display, type, name);
+
+ verify(mMockService).performAccessibilityShortcut(display, type, name);
+ }
+ }
+
private class MyAccessibilityProxy extends AccessibilityDisplayProxy {
MyAccessibilityProxy(int displayId,
@NonNull List<AccessibilityServiceInfo> serviceInfos) {
diff --git a/core/tests/coretests/src/android/view/accessibility/a11ychecker/AccessibilityNodePathBuilderTest.java b/core/tests/coretests/src/android/view/accessibility/a11ychecker/AccessibilityNodePathBuilderTest.java
new file mode 100644
index 0000000..438277b
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/a11ychecker/AccessibilityNodePathBuilderTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility.a11ychecker;
+
+import static android.view.accessibility.a11ychecker.MockAccessibilityNodeInfoBuilder.PACKAGE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.RecyclerView;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodePathBuilderTest {
+
+ public static final String RESOURCE_ID_PREFIX = PACKAGE_NAME + ":id/";
+
+ @Test
+ public void createNodePath_pathWithResourceNames() {
+ AccessibilityNodeInfo child = new MockAccessibilityNodeInfoBuilder()
+ .setViewIdResourceName(RESOURCE_ID_PREFIX + "child_node")
+ .build();
+ AccessibilityNodeInfo parent =
+ new MockAccessibilityNodeInfoBuilder()
+ .setViewIdResourceName(RESOURCE_ID_PREFIX + "parent_node")
+ .addChildren(ImmutableList.of(child))
+ .build();
+ AccessibilityNodeInfo root =
+ new MockAccessibilityNodeInfoBuilder()
+ .setViewIdResourceName(RESOURCE_ID_PREFIX + "root_node")
+ .addChildren(ImmutableList.of(parent))
+ .build();
+
+ assertThat(AccessibilityNodePathBuilder.createNodePath(child))
+ .isEqualTo(PACKAGE_NAME + ":root_node/parent_node[1]/child_node[1]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(parent))
+ .isEqualTo(PACKAGE_NAME + ":root_node/parent_node[1]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(root))
+ .isEqualTo(PACKAGE_NAME + ":root_node");
+ }
+
+ @Test
+ public void createNodePath_pathWithoutResourceNames() {
+ AccessibilityNodeInfo child =
+ new MockAccessibilityNodeInfoBuilder()
+ .setClassName(TextView.class.getName())
+ .build();
+ AccessibilityNodeInfo parent =
+
+ new MockAccessibilityNodeInfoBuilder()
+ .setClassName(RecyclerView.class.getName())
+ .addChildren(ImmutableList.of(child))
+ .build();
+ AccessibilityNodeInfo root =
+ new MockAccessibilityNodeInfoBuilder()
+ .setClassName(FrameLayout.class.getName())
+ .addChildren(ImmutableList.of(parent))
+ .build();
+
+ assertThat(AccessibilityNodePathBuilder.createNodePath(child))
+ .isEqualTo(PACKAGE_NAME + ":FrameLayout/RecyclerView[1]/TextView[1]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(parent))
+ .isEqualTo(PACKAGE_NAME + ":FrameLayout/RecyclerView[1]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(root))
+ .isEqualTo(PACKAGE_NAME + ":FrameLayout");
+ }
+
+ @Test
+ public void createNodePath_parentWithMultipleChildren() {
+ AccessibilityNodeInfo child1 =
+ new MockAccessibilityNodeInfoBuilder()
+ .setViewIdResourceName(RESOURCE_ID_PREFIX + "child1")
+ .build();
+ AccessibilityNodeInfo child2 =
+ new MockAccessibilityNodeInfoBuilder()
+ .setClassName(TextView.class.getName())
+ .build();
+ AccessibilityNodeInfo parent =
+ new MockAccessibilityNodeInfoBuilder()
+ .setClassName(FrameLayout.class.getName())
+ .addChildren(ImmutableList.of(child1, child2))
+ .build();
+
+ assertThat(AccessibilityNodePathBuilder.createNodePath(child1))
+ .isEqualTo(PACKAGE_NAME + ":FrameLayout/child1[1]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(child2))
+ .isEqualTo(PACKAGE_NAME + ":FrameLayout/TextView[2]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(parent))
+ .isEqualTo(PACKAGE_NAME + ":FrameLayout");
+ }
+
+ @Test
+ public void createNodePath_handlesDifferentIdFormats() {
+ AccessibilityNodeInfo child1 =
+ new MockAccessibilityNodeInfoBuilder()
+ .setViewIdResourceName(RESOURCE_ID_PREFIX + "childId")
+ .build();
+ AccessibilityNodeInfo child2 =
+ new MockAccessibilityNodeInfoBuilder()
+ .setViewIdResourceName(RESOURCE_ID_PREFIX + "child/Id/With/Slash")
+ .build();
+ AccessibilityNodeInfo child3 =
+ new MockAccessibilityNodeInfoBuilder()
+ .setViewIdResourceName("childIdWithoutPrefix")
+ .build();
+ AccessibilityNodeInfo parent =
+ new MockAccessibilityNodeInfoBuilder()
+ .addChildren(ImmutableList.of(child1, child2, child3))
+ .setViewIdResourceName(RESOURCE_ID_PREFIX + "parentId")
+ .build();
+
+ assertThat(AccessibilityNodePathBuilder.createNodePath(child1))
+ .isEqualTo(PACKAGE_NAME + ":parentId/childId[1]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(child2))
+ .isEqualTo(PACKAGE_NAME + ":parentId/child/Id/With/Slash[2]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(child3))
+ .isEqualTo(PACKAGE_NAME + ":parentId/childIdWithoutPrefix[3]");
+ assertThat(AccessibilityNodePathBuilder.createNodePath(parent))
+ .isEqualTo(PACKAGE_NAME + ":parentId");
+ }
+
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/a11ychecker/MockAccessibilityNodeInfoBuilder.java b/core/tests/coretests/src/android/view/accessibility/a11ychecker/MockAccessibilityNodeInfoBuilder.java
new file mode 100644
index 0000000..e363f0c
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/a11ychecker/MockAccessibilityNodeInfoBuilder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility.a11ychecker;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.util.List;
+
+final class MockAccessibilityNodeInfoBuilder {
+ static final String PACKAGE_NAME = "com.example.app";
+ private final AccessibilityNodeInfo mMockNodeInfo = mock(AccessibilityNodeInfo.class);
+
+ MockAccessibilityNodeInfoBuilder() {
+ when(mMockNodeInfo.getPackageName()).thenReturn(PACKAGE_NAME);
+ }
+
+ MockAccessibilityNodeInfoBuilder setClassName(String className) {
+ when(mMockNodeInfo.getClassName()).thenReturn(className);
+ return this;
+ }
+
+ MockAccessibilityNodeInfoBuilder setViewIdResourceName(String
+ viewIdResourceName) {
+ when(mMockNodeInfo.getViewIdResourceName()).thenReturn(viewIdResourceName);
+ return this;
+ }
+
+ MockAccessibilityNodeInfoBuilder addChildren(List<AccessibilityNodeInfo>
+ children) {
+ when(mMockNodeInfo.getChildCount()).thenReturn(children.size());
+ for (int i = 0; i < children.size(); i++) {
+ when(mMockNodeInfo.getChild(i)).thenReturn(children.get(i));
+ when(children.get(i).getParent()).thenReturn(mMockNodeInfo);
+ }
+ return this;
+ }
+
+ AccessibilityNodeInfo build() {
+ return mMockNodeInfo;
+ }
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/a11ychecker/OWNERS b/core/tests/coretests/src/android/view/accessibility/a11ychecker/OWNERS
new file mode 100644
index 0000000..872a180
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/a11ychecker/OWNERS
@@ -0,0 +1,5 @@
+# Android Accessibility Framework owners
+include /core/java/android/view/accessibility/a11ychecker/OWNERS
+include /services/accessibility/OWNERS
+
+yaraabdullatif@google.com
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 6ab77dc..534420e 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -355,7 +355,8 @@
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize,
+ int capBitmapSize) {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
new RemoteViews.RemoteCollectionItems.Builder();
itemsBuilder.setHasStableIds(hasStableIds())
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
new file mode 100644
index 0000000..8e9ba7b
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/RemoteViewsProtoTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.util.SizeF;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.view.View;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+/**
+ * Tests for RemoteViews.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RemoteViewsProtoTest {
+
+ // This can point to any other package which exists on the device.
+ private static final String OTHER_PACKAGE = "com.android.systemui";
+
+ @Rule
+ public final ExpectedException exception = ExpectedException.none();
+
+ private Context mContext;
+ private String mPackage;
+ private LinearLayout mContainer;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getContext();
+ mPackage = mContext.getPackageName();
+ mContainer = new LinearLayout(mContext);
+ }
+
+ @Test
+ public void copy_canStillBeApplied() {
+ RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+ RemoteViews clone = recreateFromProto(original);
+
+ clone.apply(mContext, mContainer);
+ }
+
+ @SuppressWarnings("ReturnValueIgnored")
+ @Test
+ public void clone_repeatedly() {
+ RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+ recreateFromProto(original);
+ recreateFromProto(original);
+
+ original.apply(mContext, mContainer);
+ }
+
+ @Test
+ public void clone_chained() {
+ RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test);
+
+ RemoteViews clone = recreateFromProto(recreateFromProto(original));
+
+
+ clone.apply(mContext, mContainer);
+ }
+
+ @Test
+ public void landscapePortraitViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(inner, inner);
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = recreateFromProto(parent).apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+ @Test
+ public void sizedViews_lightBackgroundLayoutFlag() {
+ RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
+ inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
+
+ RemoteViews parent = new RemoteViews(
+ Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
+ parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+
+ View view = recreateFromProto(parent).apply(mContext, mContainer);
+ assertNull(view.findViewById(R.id.text));
+ assertNotNull(view.findViewById(R.id.light_background_text));
+ }
+
+ @Test
+ public void nestedLandscapeViews() throws Exception {
+ RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+ for (int i = 0; i < 10; i++) {
+ views = new RemoteViews(views, new RemoteViews(mPackage, R.layout.remote_views_test));
+ }
+ // writeTo/createFromProto works
+ recreateFromProto(views);
+
+ views = new RemoteViews(mPackage, R.layout.remote_views_test);
+ for (int i = 0; i < 11; i++) {
+ views = new RemoteViews(views, new RemoteViews(mPackage, R.layout.remote_views_test));
+ }
+ // writeTo/createFromProto fails
+ exception.expect(IllegalArgumentException.class);
+ recreateFromProtoNoRethrow(views);
+ }
+
+ private RemoteViews recreateFromProto(RemoteViews views) {
+ try {
+ return recreateFromProtoNoRethrow(views);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private RemoteViews recreateFromProtoNoRethrow(RemoteViews views) throws Exception {
+ ProtoOutputStream out = new ProtoOutputStream();
+ views.writePreviewToProto(mContext, out);
+ ProtoInputStream in = new ProtoInputStream(out.getBytes());
+ return RemoteViews.createPreviewFromProto(mContext, in);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 6b9dbba..f78bc92 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -71,6 +71,7 @@
import android.speech.tts.TextToSpeech;
import android.speech.tts.Voice;
import android.test.mock.MockContentResolver;
+import android.view.Display;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -378,7 +379,8 @@
verify(mAlertDialog).show();
verify(mAccessibilityManagerService, atLeastOnce()).getInstalledAccessibilityServiceList(
anyInt());
- verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
verify(mFrameworkObjectProvider, times(0)).getTextToSpeech(any(), any());
}
@@ -397,7 +399,8 @@
// assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
// mLayoutParams.privateFlags
// & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
- verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -630,7 +633,8 @@
verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
verify(mToast).show();
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -649,7 +653,8 @@
verify(mAccessibilityManagerService).enableShortcutsForTargets(
eq(true), eq(HARDWARE), mListCaptor.capture(), anyInt());
assertThat(mListCaptor.getValue()).containsExactly(SERVICE_NAME_STRING);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -666,7 +671,8 @@
assertThat(Settings.Secure.getString(mContentResolver,
ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -726,7 +732,8 @@
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
AccessibilityShortcutController.DialogStatus.SHOWN);
getController().performAccessibilityShortcut();
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -739,7 +746,8 @@
getController().performAccessibilityShortcut();
verifyZeroInteractions(mToast);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
@@ -755,7 +763,8 @@
getController().performAccessibilityShortcut();
verifyZeroInteractions(mToast);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(null);
+ verify(mAccessibilityManagerService).performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE, null);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetHelperTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetHelperTest.java
new file mode 100644
index 0000000..64be97d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetHelperTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityTargetHelperTest {
+ @Test
+ public void isValidServiceTarget_legacyService_hardware_isTrue() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ /* flags = */ 0, /* targetSdk = */ 0), HARDWARE)).isTrue();
+ }
+
+ @Test
+ public void isValidServiceTarget_legacyService_software_requestsButton_isTrue() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ FLAG_REQUEST_ACCESSIBILITY_BUTTON, /* targetSdk = */ 0), SOFTWARE)).isTrue();
+ }
+
+ @Test
+ public void isValidServiceTarget_legacyService_software_isFalse() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ /* flags = */ 0, /* targetSdk = */ 0), SOFTWARE)).isFalse();
+ }
+
+ @Test
+ public void isValidServiceTarget_modernService_isTrue() {
+ assertThat(AccessibilityTargetHelper.isValidServiceTarget(generateServiceInfo(
+ /* flags = */ 0, Build.VERSION_CODES.Q + 1), DEFAULT)).isTrue();
+ }
+
+ private AccessibilityServiceInfo generateServiceInfo(int flags, int targetSdk) {
+ AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ info.flags = flags;
+ info.setResolveInfo(new ResolveInfo());
+ info.getResolveInfo().serviceInfo = new ServiceInfo();
+ info.getResolveInfo().serviceInfo.applicationInfo = new ApplicationInfo();
+ info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = targetSdk;
+ return info;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
new file mode 100644
index 0000000..389b677
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/AccessibilityTargetTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.common.ShortcutConstants;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.stream.IntStream;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityTargetTest {
+ private static final int[] EXPECTED_TYPES = { HARDWARE, SOFTWARE };
+
+ @Test
+ public void isRecognizedShortcutType_expectedType_isTrue() {
+ for (int type : EXPECTED_TYPES) {
+ assertThat(AccessibilityTarget.isRecognizedShortcutType(type)).isTrue();
+ }
+ }
+
+ @Test
+ public void isRecognizedShortcutType_notExpectedType_isFalse() {
+ for (int type: ShortcutConstants.USER_SHORTCUT_TYPES) {
+ if (IntStream.of(EXPECTED_TYPES).noneMatch(x -> x == type)) {
+ assertThat(AccessibilityTarget.isRecognizedShortcutType(type)).isFalse();
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
index 708f246..928fce9 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
@@ -25,33 +25,28 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.pm.ParceledListSlice;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.provider.Settings;
+import android.testing.TestableContext;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.accessibility.TestUtils;
import com.android.internal.accessibility.common.ShortcutConstants;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.internal.util.test.FakeSettingsProviderRule;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -79,39 +74,33 @@
private static final String STANDARD_SERVICE_COMPONENT_NAME =
"fake.package/fake.standard.service.name";
private static final String SERVICE_NAME_SUMMARY = "Summary";
-
- @Rule
- public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock
private IAccessibilityManager mAccessibilityManagerService;
- private ContextWrapper mContextSpy;
+ private TestableContext mContext;
+ @UserIdInt
+ private int mDefaultUserId;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mContextSpy = spy(
- new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
-
- ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
- when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ mContext = new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
+ mDefaultUserId = mContext.getContentResolver().getUserId();
AccessibilityManager accessibilityManager =
new AccessibilityManager(
- mContextSpy, mock(Handler.class),
- mAccessibilityManagerService, UserHandle.myUserId(),
+ mContext, mock(Handler.class),
+ mAccessibilityManagerService, mDefaultUserId,
/* serviceConnect= */ true);
- when(mContextSpy.getSystemService(Context.ACCESSIBILITY_SERVICE))
- .thenReturn(accessibilityManager);
-
- setupFakeA11yServiceInfos();
+ mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, accessibilityManager);
+ setupFakeInstalledA11yServiceInfos();
}
@Test
public void getShortcutTargets_softwareShortcutNoService_emptyResult() {
assertThat(
ShortcutUtils.getShortcutTargetsFromSettings(
- mContextSpy,
- ShortcutConstants.UserShortcutType.SOFTWARE, UserHandle.myUserId())
+ mContext,
+ ShortcutConstants.UserShortcutType.SOFTWARE, mDefaultUserId)
).isEmpty();
}
@@ -119,8 +108,8 @@
public void getShortcutTargets_volumeKeyShortcutNoService_emptyResult() {
assertThat(
ShortcutUtils.getShortcutTargetsFromSettings(
- mContextSpy, ShortcutConstants.UserShortcutType.HARDWARE,
- UserHandle.myUserId())
+ mContext, ShortcutConstants.UserShortcutType.HARDWARE,
+ mDefaultUserId)
).isEmpty();
}
@@ -131,8 +120,8 @@
assertThat(
ShortcutUtils.getShortcutTargetsFromSettings(
- mContextSpy, ShortcutConstants.UserShortcutType.SOFTWARE,
- UserHandle.myUserId())
+ mContext, ShortcutConstants.UserShortcutType.SOFTWARE,
+ mDefaultUserId)
).containsExactlyElementsIn(ONE_COMPONENT);
}
@@ -143,8 +132,8 @@
assertThat(
ShortcutUtils.getShortcutTargetsFromSettings(
- mContextSpy, ShortcutConstants.UserShortcutType.HARDWARE,
- UserHandle.myUserId())
+ mContext, ShortcutConstants.UserShortcutType.HARDWARE,
+ mDefaultUserId)
).containsExactlyElementsIn(TWO_COMPONENTS);
}
@@ -156,8 +145,8 @@
assertThat(
ShortcutUtils.getShortcutTargetsFromSettings(
- mContextSpy, ShortcutConstants.UserShortcutType.TRIPLETAP,
- UserHandle.myUserId())
+ mContext, ShortcutConstants.UserShortcutType.TRIPLETAP,
+ mDefaultUserId)
).isEmpty();
}
@@ -169,8 +158,8 @@
assertThat(
ShortcutUtils.getShortcutTargetsFromSettings(
- mContextSpy, ShortcutConstants.UserShortcutType.TRIPLETAP,
- UserHandle.myUserId())
+ mContext, ShortcutConstants.UserShortcutType.TRIPLETAP,
+ mDefaultUserId)
).containsExactly(ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER);
}
@@ -180,23 +169,46 @@
ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ false);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
}
@Test
+ public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOnForBothUsers_noShortcutsForGuestUser_serviceTurnedOffForGuestUserOnly() {
+ // setup arbitrary userId by add 10 to the default user id
+ final int guestUserId = mDefaultUserId + 10;
+ setupA11yServiceAndShortcutStateForUser(
+ ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ true,
+ /* shortcutOn= */ true, mDefaultUserId);
+ setupA11yServiceAndShortcutStateForUser(
+ ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ true,
+ /* shortcutOn= */ false, guestUserId);
+
+ ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+ mContext,
+ Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
+ guestUserId
+ );
+
+ assertA11yServiceStateForUser(
+ ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false, guestUserId);
+ assertA11yServiceStateForUser(
+ ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true, mDefaultUserId);
+ }
+
+ @Test
public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOn_hasShortcut_serviceKeepsOn() {
setupA11yServiceAndShortcutState(
ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ true);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
@@ -208,9 +220,9 @@
ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ false);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
@@ -222,9 +234,9 @@
ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ true);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
@@ -236,9 +248,9 @@
STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ false);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(STANDARD_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
@@ -250,9 +262,9 @@
STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ true);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(STANDARD_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
@@ -264,9 +276,9 @@
STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ false);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(STANDARD_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
@@ -278,9 +290,9 @@
STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ true);
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContextSpy,
+ mContext,
Set.of(STANDARD_SERVICE_COMPONENT_NAME),
- UserHandle.myUserId()
+ mDefaultUserId
);
assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
@@ -292,18 +304,18 @@
stringJoiner.add(target);
}
Settings.Secure.putStringForUser(
- mContextSpy.getContentResolver(), shortcutSettingsKey,
+ mContext.getContentResolver(), shortcutSettingsKey,
stringJoiner.toString(),
- UserHandle.myUserId());
+ mDefaultUserId);
}
private void enableTripleTapShortcutForMagnification(boolean enable) {
Settings.Secure.putInt(
- mContextSpy.getContentResolver(),
+ mContext.getContentResolver(),
ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, enable ? 1 : 0);
}
- private void setupFakeA11yServiceInfos() throws RemoteException {
+ private void setupFakeInstalledA11yServiceInfos() throws RemoteException {
List<AccessibilityServiceInfo> serviceInfos = List.of(
TestUtils.createFakeServiceInfo(
ALWAYS_ON_SERVICE_PACKAGE_LABEL,
@@ -322,37 +334,55 @@
private void setupA11yServiceAndShortcutState(
String a11yServiceComponentName, boolean serviceOn, boolean shortcutOn) {
- enableA11yService(a11yServiceComponentName, serviceOn);
- addShortcutForA11yService(a11yServiceComponentName, shortcutOn);
+ setupA11yServiceAndShortcutStateForUser(
+ a11yServiceComponentName, serviceOn, shortcutOn, mDefaultUserId);
+ }
+
+ private void setupA11yServiceAndShortcutStateForUser(
+ String a11yServiceComponentName, boolean serviceOn,
+ boolean shortcutOn, @UserIdInt int userId) {
+ enableA11yServiceForUser(a11yServiceComponentName, serviceOn, userId);
+ addShortcutForA11yServiceForUser(a11yServiceComponentName, shortcutOn, userId);
}
private void assertA11yServiceState(String a11yServiceComponentName, boolean enabled) {
+ assertA11yServiceStateForUser(a11yServiceComponentName, enabled, mDefaultUserId);
+ }
+
+ private void assertA11yServiceStateForUser(
+ String a11yServiceComponentName, boolean enabled, @UserIdInt int userId) {
if (enabled) {
assertThat(
- Settings.Secure.getString(
- mContextSpy.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userId)
).contains(a11yServiceComponentName);
} else {
assertThat(
- Settings.Secure.getString(
- mContextSpy.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
+ Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userId)
).doesNotContain(a11yServiceComponentName);
}
}
- private void enableA11yService(String a11yServiceComponentName, boolean enable) {
- Settings.Secure.putString(
- mContextSpy.getContentResolver(),
+ private void enableA11yServiceForUser(
+ String a11yServiceComponentName, boolean enable, @UserIdInt int userId) {
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- enable ? a11yServiceComponentName : "");
+ enable ? a11yServiceComponentName : "",
+ userId);
}
- private void addShortcutForA11yService(String a11yServiceComponentName, boolean add) {
- Settings.Secure.putString(
- mContextSpy.getContentResolver(),
+ private void addShortcutForA11yServiceForUser(
+ String a11yServiceComponentName, boolean add, @UserIdInt int userId) {
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- add ? a11yServiceComponentName : "");
+ add ? a11yServiceComponentName : "",
+ userId);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
index 06d888b..7ffc7b2 100644
--- a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
@@ -77,7 +76,6 @@
}
@Test
- @IgnoreUnderRavenwood(reason = "b/321832617")
public void corruptedFile() throws IOException {
// Create an invalid binary XML file to cause IOException: "Unexpected magic number"
try (FileWriter w = new FileWriter(mFile)) {
diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
index ac659e1..6c6feaf 100644
--- a/core/tests/utiltests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
@@ -18,17 +18,19 @@
import static org.junit.Assert.assertEquals;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
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;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.TimeZone;
import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
@@ -42,6 +44,22 @@
public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+ private TimeZone mOrigTimezone;
+
+ @Before
+ public void setUp() {
+ mOrigTimezone = TimeZone.getDefault();
+
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ }
+
+ @After
+ public void tearDown() {
+ if (mOrigTimezone != null) {
+ TimeZone.setDefault(mOrigTimezone);
+ }
+ }
+
@Test
public void testFormatTime() {
assertEquals("1672556400000 (now)",
@@ -85,32 +103,29 @@
}
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testDumpTime() {
- assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> {
+ assertEquals("2023-01-01 07:00:00.000", runWithPrintWriter((pw) -> {
TimeUtils.dumpTime(pw, 1672556400000L);
}));
- assertEquals("2023-01-01 00:00:00.000 (now)", runWithPrintWriter((pw) -> {
+ assertEquals("2023-01-01 07:00:00.000 (now)", runWithPrintWriter((pw) -> {
TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L);
}));
- assertEquals("2023-01-01 00:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
+ assertEquals("2023-01-01 07:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L + 10);
}));
}
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testFormatForLogging() {
assertEquals("unknown", TimeUtils.formatForLogging(0));
assertEquals("unknown", TimeUtils.formatForLogging(-1));
assertEquals("unknown", TimeUtils.formatForLogging(Long.MIN_VALUE));
- assertEquals("2023-01-01 00:00:00", TimeUtils.formatForLogging(1672556400000L));
+ assertEquals("2023-01-01 07:00:00", TimeUtils.formatForLogging(1672556400000L));
}
@Test
- @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
public void testLogTimeOfDay() {
- assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
+ assertEquals("01-01 07:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
}
public static String runWithPrintWriter(Consumer<PrintWriter> consumer) {
diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java
index 94298dc..83a8f8f 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/ParsedVibrationTest.java
@@ -63,6 +63,34 @@
}
@Test
+ public void testEquals() {
+ assertThat(new ParsedVibration(List.of())).isEqualTo(new ParsedVibration(List.of()));
+ assertThat(new ParsedVibration(List.of())).isNotEqualTo(new ParsedVibration(mEffect1));
+ assertThat(new ParsedVibration(mEffect1)).isEqualTo(new ParsedVibration(mEffect1));
+ assertThat(new ParsedVibration(mEffect1)).isNotEqualTo(new ParsedVibration(mEffect2));
+ assertThat(new ParsedVibration(List.of(mEffect1, mEffect2, mEffect3)))
+ .isEqualTo(new ParsedVibration(List.of(mEffect1, mEffect2, mEffect3)));
+ assertThat(new ParsedVibration(List.of(mEffect1, mEffect2)))
+ .isNotEqualTo(new ParsedVibration(List.of(mEffect2, mEffect1)));
+ }
+
+ @Test
+ public void testHashCode() {
+ assertThat(new ParsedVibration(mEffect1).hashCode())
+ .isEqualTo(new ParsedVibration(mEffect1).hashCode());
+ assertThat(new ParsedVibration(mEffect1).hashCode())
+ .isNotEqualTo(new ParsedVibration(mEffect2).hashCode());
+ assertThat(new ParsedVibration(List.of()).hashCode())
+ .isEqualTo(new ParsedVibration(List.of()).hashCode());
+ assertThat(new ParsedVibration(List.of()).hashCode())
+ .isNotEqualTo(new ParsedVibration(mEffect1).hashCode());
+ assertThat(new ParsedVibration(List.of(mEffect1, mEffect2, mEffect3)).hashCode())
+ .isEqualTo(new ParsedVibration(List.of(mEffect1, mEffect2, mEffect3)).hashCode());
+ assertThat(new ParsedVibration(List.of(mEffect1, mEffect2)).hashCode())
+ .isNotEqualTo(new ParsedVibration(List.of(mEffect2, mEffect1)).hashCode());
+ }
+
+ @Test
public void testResolve_allUnsupportedVibrations() {
when(mVibratorInfoMock.areVibrationFeaturesSupported(any())).thenReturn(false);
@@ -91,21 +119,6 @@
.isEqualTo(mEffect1);
}
- @Test
- public void testGetVibrationEffects() {
- ParsedVibration parsedVibration =
- new ParsedVibration(List.of(mEffect1, mEffect2, mEffect3));
- assertThat(parsedVibration.getVibrationEffects())
- .containsExactly(mEffect1, mEffect2, mEffect3)
- .inOrder();
-
- parsedVibration = new ParsedVibration(List.of(mEffect1));
- assertThat(parsedVibration.getVibrationEffects()).containsExactly(mEffect1);
-
- parsedVibration = new ParsedVibration(List.of());
- assertThat(parsedVibration.getVibrationEffects()).isEmpty();
- }
-
private Subject assertThatResolution(
Vibrator vibrator, List<VibrationEffect> componentVibrations) {
return assertThat(new ParsedVibration(componentVibrations).resolve(vibrator));
diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index 7d8c53f..bf9a820 100644
--- a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -37,9 +37,9 @@
import org.junit.runners.JUnit4;
import org.xmlpull.v1.XmlPullParser;
-import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -74,18 +74,22 @@
.addPrimitive(PRIMITIVE_CLICK)
.addPrimitive(PRIMITIVE_TICK, 0.2497f)
.compose();
- String xml = "<vibration-effect>"
- + "<primitive-effect name=\"click\"/>"
- + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
- + "</vibration-effect>";
+ String xml = """
+ <vibration-effect>
+ <primitive-effect name="click"/>
+ <primitive-effect name="tick" scale="0.2497"/>
+ </vibration-effect>
+ """.trim();
VibrationEffect effect2 = VibrationEffect.startComposition()
.addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
.addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
.compose();
- String xml2 = "<vibration-effect>"
- + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
- + "<primitive-effect name=\"spin\" scale=\"0.6364\" delayMs=\"7\"/>"
- + "</vibration-effect>";
+ String xml2 = """
+ <vibration-effect>
+ <primitive-effect name="low_tick" delayMs="356"/>
+ <primitive-effect name="spin" scale="0.6364" delayMs="7"/>
+ </vibration-effect>
+ """.trim();
TypedXmlPullParser parser = createXmlPullParser(xml);
assertParseElementSucceeds(parser, effect);
@@ -114,7 +118,12 @@
assertEndOfDocument(parser);
// Check when there is comment before the end tag.
- xml = "<vibration-effect><primitive-effect name=\"tick\"/><!-- hi --></vibration-effect>";
+ xml = """
+ <vibration-effect>
+ <primitive-effect name="tick"/>
+ <!-- hi -->
+ </vibration-effect>
+ """.trim();
parser = createXmlPullParser(xml);
assertParseElementSucceeds(
parser, VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK).compose());
@@ -128,18 +137,22 @@
.addPrimitive(PRIMITIVE_CLICK)
.addPrimitive(PRIMITIVE_TICK, 0.2497f)
.compose();
- String vibrationXml1 = "<vibration-effect>"
- + "<primitive-effect name=\"click\"/>"
- + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
- + "</vibration-effect>";
+ String vibrationXml1 = """
+ <vibration-effect>
+ <primitive-effect name="click"/>
+ <primitive-effect name="tick" scale="0.2497"/>
+ </vibration-effect>
+ """.trim();
VibrationEffect effect2 = VibrationEffect.startComposition()
.addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
.addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
.compose();
- String vibrationXml2 = "<vibration-effect>"
- + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
- + "<primitive-effect name=\"spin\" scale=\"0.6364\" delayMs=\"7\"/>"
- + "</vibration-effect>";
+ String vibrationXml2 = """
+ <vibration-effect>
+ <primitive-effect name="low_tick" delayMs="356"/>
+ <primitive-effect name="spin" scale="0.6364" delayMs="7"/>
+ </vibration-effect>
+ """.trim();
String xml = "<vibration-select>" + vibrationXml1 + vibrationXml2 + "</vibration-select>";
TypedXmlPullParser parser = createXmlPullParser(xml);
@@ -183,8 +196,11 @@
@Test
public void testParseElement_withHiddenApis_onlySucceedsWithFlag() throws Exception {
// Check when the root tag is "vibration".
- String xml =
- "<vibration-effect><predefined-effect name=\"texture_tick\"/></vibration-effect>";
+ String xml = """
+ <vibration-effect>
+ <predefined-effect name="texture_tick"/>
+ </vibration-effect>
+ """.trim();
assertParseElementSucceeds(createXmlPullParser(xml),
VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS,
VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK));
@@ -199,131 +215,186 @@
}
@Test
- public void testParseElement_badXml_throwsException() throws Exception {
+ public void testParseElement_badXml_throwsException() {
// No "vibration-select" tag.
- assertParseElementFails(
- "<vibration-effect>rand text<primitive-effect name=\"click\"/></vibration-effect>");
- assertParseElementFails("<bad-tag><primitive-effect name=\"click\"/></vibration-effect>");
- assertParseElementFails("<primitive-effect name=\"click\"/></vibration-effect>");
- assertParseElementFails("<vibration-effect><primitive-effect name=\"click\"/>");
+ assertParseElementFails("""
+ <vibration-effect>
+ rand text
+ <primitive-effect name="click"/>
+ </vibration-effect>
+ """);
+ assertParseElementFails("""
+ <bad-tag>
+ <primitive-effect name="click"/>
+ </vibration-effect>
+ """);
+ assertParseElementFails("""
+ <primitive-effect name="click"/>
+ </vibration-effect>
+ """);
+ assertParseElementFails("""
+ <vibration-effect>
+ <primitive-effect name="click"/>
+ """);
// Incomplete XML.
- assertParseElementFails("<vibration-select><primitive-effect name=\"click\"/>");
- assertParseElementFails("<vibration-select>"
- + "<vibration-effect>"
- + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
- + "</vibration-effect>");
+ assertParseElementFails("""
+ <vibration-select>
+ <primitive-effect name="click"/>
+ """);
+ assertParseElementFails("""
+ <vibration-select>
+ <vibration-effect>
+ <primitive-effect name="low_tick" delayMs="356"/>
+ </vibration-effect>
+ """);
// Bad vibration XML.
- assertParseElementFails("<vibration-select>"
- + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
- + "</vibration-effect>"
- + "</vibration-select>");
+ assertParseElementFails("""
+ <vibration-select>
+ <primitive-effect name="low_tick" delayMs="356"/>
+ </vibration-effect>
+ </vibration-select>
+ """);
// "vibration-select" tag should have no attributes.
- assertParseElementFails("<vibration-select bad_attr=\"123\">"
- + "<vibration-effect>"
- + "<predefined-effect name=\"tick\"/>"
- + "</vibration-effect>"
- + "</vibration-select>");
+ assertParseElementFails("""
+ <vibration-select bad_attr="123">
+ <vibration-effect>
+ <predefined-effect name="tick"/>
+ </vibration-effect>
+ </vibration-select>
+ """);
}
@Test
- public void testPrimitives_allSucceed() throws IOException {
+ public void testInvalidEffects_allFail() {
+ // Invalid root tag.
+ String xml = """
+ <vibration>
+ <predefined-effect name="click"/>
+ </vibration>
+ """;
+
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+
+ // Invalid effect name.
+ xml = """
+ <vibration-effect>
+ <predefined-effect name="invalid"/>
+ </vibration-effect>
+ """;
+
+ assertPublicApisParserFails(xml);
+ assertHiddenApisParserFails(xml);
+ }
+
+ @Test
+ public void testVibrationSelectTag_onlyParseDocumentSucceeds() throws Exception {
+ VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ String xml = """
+ <vibration-select>
+ <vibration-effect><predefined-effect name="click"/></vibration-effect>
+ </vibration-select>
+ """;
+
+ assertPublicApisParseDocumentSucceeds(xml, effect);
+ assertHiddenApisParseDocumentSucceeds(xml, effect);
+
+ assertPublicApisParseVibrationEffectFails(xml);
+ assertHiddenApisParseVibrationEffectFails(xml);
+ }
+
+ @Test
+ public void testPrimitives_allSucceed() throws Exception {
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(PRIMITIVE_CLICK)
.addPrimitive(PRIMITIVE_TICK, 0.2497f)
.addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
.addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
.compose();
- String xml = "<vibration-effect>"
- + "<primitive-effect name=\"click\"/>"
- + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
- + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
- + "<primitive-effect name=\"spin\" scale=\"0.6364\" delayMs=\"7\"/>"
- + "</vibration-effect>";
+ String xml = """
+ <vibration-effect>
+ <primitive-effect name="click"/>
+ <primitive-effect name="tick" scale="0.2497"/>
+ <primitive-effect name="low_tick" delayMs="356"/>
+ <primitive-effect name="spin" scale="0.6364" delayMs="7"/>
+ </vibration-effect>
+ """;
assertPublicApisParserSucceeds(xml, effect);
assertPublicApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin");
assertPublicApisRoundTrip(effect);
- assertHiddenApisParseVibrationEffectSucceeds(xml, effect);
+ assertHiddenApisParserSucceeds(xml, effect);
assertHiddenApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin");
assertHiddenApisRoundTrip(effect);
}
@Test
- public void testParseDocument_withVibrationSelectTag_withHiddenApis_onlySucceedsWithFlag()
- throws Exception {
- // Check when the root tag is "vibration-effect".
- String xml =
- "<vibration-effect><predefined-effect name=\"texture_tick\"/></vibration-effect>";
- assertParseDocumentSucceeds(xml,
- VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS,
- VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK));
- assertThat(parseDocument(xml, /* flags= */ 0)).isNull();
-
- // Check when the root tag is "vibration-select".
- xml = "<vibration-select>" + xml + "</vibration-select>";
- assertParseDocumentSucceeds(xml,
- VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS,
- VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK));
- assertThat(parseDocument(xml, /* flags= */ 0)).isNull();
- }
-
- @Test
- public void testWaveforms_allSucceed() throws IOException {
+ public void testWaveforms_allSucceed() throws Exception {
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{123, 456, 789, 0},
new int[]{254, 1, 255, 0}, /* repeat= */ 0);
- String xml = "<vibration-effect>"
- + "<waveform-effect><repeating>"
- + "<waveform-entry durationMs=\"123\" amplitude=\"254\"/>"
- + "<waveform-entry durationMs=\"456\" amplitude=\"1\"/>"
- + "<waveform-entry durationMs=\"789\" amplitude=\"255\"/>"
- + "<waveform-entry durationMs=\"0\" amplitude=\"0\"/>"
- + "</repeating></waveform-effect>"
- + "</vibration-effect>";
+ String xml = """
+ <vibration-effect>
+ <waveform-effect>
+ <repeating>
+ <waveform-entry durationMs="123" amplitude="254"/>
+ <waveform-entry durationMs="456" amplitude="1"/>
+ <waveform-entry durationMs="789" amplitude="255"/>
+ <waveform-entry durationMs="0" amplitude="0"/>
+ </repeating>
+ </waveform-effect>
+ </vibration-effect>
+ """;
assertPublicApisParserSucceeds(xml, effect);
assertPublicApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0");
assertPublicApisRoundTrip(effect);
- assertHiddenApisParseVibrationEffectSucceeds(xml, effect);
+ assertHiddenApisParserSucceeds(xml, effect);
assertHiddenApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0");
assertHiddenApisRoundTrip(effect);
}
@Test
public void testPredefinedEffects_publicEffectsWithDefaultFallback_allSucceed()
- throws IOException {
+ throws Exception {
for (Map.Entry<String, Integer> entry : createPublicPredefinedEffectsMap().entrySet()) {
VibrationEffect effect = VibrationEffect.get(entry.getValue());
- String xml = String.format(
- "<vibration-effect><predefined-effect name=\"%s\"/></vibration-effect>",
+ String xml = String.format("""
+ <vibration-effect>
+ <predefined-effect name="%s"/>
+ </vibration-effect>
+ """,
entry.getKey());
assertPublicApisParserSucceeds(xml, effect);
assertPublicApisSerializerSucceeds(effect, entry.getKey());
assertPublicApisRoundTrip(effect);
- assertHiddenApisParseVibrationEffectSucceeds(xml, effect);
+ assertHiddenApisParserSucceeds(xml, effect);
assertHiddenApisSerializerSucceeds(effect, entry.getKey());
assertHiddenApisRoundTrip(effect);
}
}
@Test
- public void testPredefinedEffects_hiddenEffects_onlySucceedsWithFlag() throws IOException {
+ public void testPredefinedEffects_hiddenEffects_onlySucceedsWithFlag() throws Exception {
for (Map.Entry<String, Integer> entry : createHiddenPredefinedEffectsMap().entrySet()) {
VibrationEffect effect = VibrationEffect.get(entry.getValue());
- String xml = String.format(
- "<vibration-effect><predefined-effect name=\"%s\"/></vibration-effect>",
+ String xml = String.format("""
+ <vibration-effect>
+ <predefined-effect name="%s"/>
+ </vibration-effect>
+ """,
entry.getKey());
assertPublicApisParserFails(xml);
assertPublicApisSerializerFails(effect);
- assertHiddenApisParseVibrationEffectSucceeds(xml, effect);
+ assertHiddenApisParserSucceeds(xml, effect);
assertHiddenApisSerializerSucceeds(effect, entry.getKey());
assertHiddenApisRoundTrip(effect);
}
@@ -331,33 +402,119 @@
@Test
public void testPredefinedEffects_allEffectsWithNonDefaultFallback_onlySucceedsWithFlag()
- throws IOException {
+ throws Exception {
for (Map.Entry<String, Integer> entry : createAllPredefinedEffectsMap().entrySet()) {
boolean nonDefaultFallback = !PrebakedSegment.DEFAULT_SHOULD_FALLBACK;
VibrationEffect effect = VibrationEffect.get(entry.getValue(), nonDefaultFallback);
- String xml = String.format(
- "<vibration-effect><predefined-effect name=\"%s\" fallback=\"%s\"/>"
- + "</vibration-effect>",
+ String xml = String.format("""
+ <vibration-effect>
+ <predefined-effect name="%s" fallback="%s"/>
+ </vibration-effect>
+ """,
entry.getKey(), nonDefaultFallback);
assertPublicApisParserFails(xml);
assertPublicApisSerializerFails(effect);
- assertHiddenApisParseVibrationEffectSucceeds(xml, effect);
+ assertHiddenApisParserSucceeds(xml, effect);
assertHiddenApisSerializerSucceeds(effect, entry.getKey());
assertHiddenApisRoundTrip(effect);
}
}
- private void assertPublicApisParserFails(String xml) throws IOException {
- assertThat(parseVibrationEffect(xml, /* flags= */ 0)).isNull();
+ private void assertPublicApisParserFails(String xml) {
+ assertThrows("Expected parseVibrationEffect to fail for " + xml,
+ VibrationXmlParser.ParseFailedException.class,
+ () -> parseVibrationEffect(xml, /* flags= */ 0));
+ assertThrows("Expected parseDocument to fail for " + xml,
+ VibrationXmlParser.ParseFailedException.class,
+ () -> parseDocument(xml, /* flags= */ 0));
+ }
+
+ private void assertPublicApisParseVibrationEffectFails(String xml) {
+ assertThrows("Expected parseVibrationEffect to fail for " + xml,
+ VibrationXmlParser.ParseFailedException.class,
+ () -> parseVibrationEffect(xml, /* flags= */ 0));
}
private void assertPublicApisParserSucceeds(String xml, VibrationEffect effect)
- throws IOException {
+ throws Exception {
+ assertPublicApisParseDocumentSucceeds(xml, effect);
+ assertPublicApisParseVibrationEffectSucceeds(xml, effect);
+ }
+
+ private void assertPublicApisParseDocumentSucceeds(String xml, VibrationEffect... effects)
+ throws Exception {
+ assertThat(parseDocument(xml, /* flags= */ 0))
+ .isEqualTo(new ParsedVibration(Arrays.asList(effects)));
+ }
+
+ private void assertPublicApisParseVibrationEffectSucceeds(String xml, VibrationEffect effect)
+ throws Exception {
assertThat(parseVibrationEffect(xml, /* flags= */ 0)).isEqualTo(effect);
}
+ private void assertHiddenApisParserFails(String xml) {
+ assertThrows("Expected parseVibrationEffect to fail for " + xml,
+ VibrationXmlParser.ParseFailedException.class,
+ () -> parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS));
+ assertThrows("Expected parseDocument to fail for " + xml,
+ VibrationXmlParser.ParseFailedException.class,
+ () -> parseDocument(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS));
+ }
+
+ private void assertHiddenApisParseVibrationEffectFails(String xml) {
+ assertThrows("Expected parseVibrationEffect to fail for " + xml,
+ VibrationXmlParser.ParseFailedException.class,
+ () -> parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS));
+ }
+
+ private void assertHiddenApisParserSucceeds(String xml, VibrationEffect effect)
+ throws Exception {
+ assertHiddenApisParseDocumentSucceeds(xml, effect);
+ assertHiddenApisParseVibrationEffectSucceeds(xml, effect);
+ }
+
+ private void assertHiddenApisParseDocumentSucceeds(String xml, VibrationEffect... effect)
+ throws Exception {
+ assertThat(parseDocument(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS))
+ .isEqualTo(new ParsedVibration(Arrays.asList(effect)));
+ }
+
+ private void assertHiddenApisParseVibrationEffectSucceeds(String xml, VibrationEffect effect)
+ throws Exception {
+ assertThat(parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS))
+ .isEqualTo(effect);
+ }
+
+ private void assertPublicApisSerializerFails(VibrationEffect effect) {
+ assertThrows("Expected serialization to fail for " + effect,
+ VibrationXmlSerializer.SerializationFailedException.class,
+ () -> serialize(effect));
+ }
+
+ private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
+ String... expectedSegments) throws Exception {
+ assertSerializationContainsSegments(serialize(effect), expectedSegments);
+ }
+
+ private void assertHiddenApisSerializerSucceeds(VibrationEffect effect,
+ String... expectedSegments) throws Exception {
+ assertSerializationContainsSegments(
+ serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS), expectedSegments);
+ }
+
+ private void assertPublicApisRoundTrip(VibrationEffect effect) throws Exception {
+ assertThat(parseVibrationEffect(serialize(effect, /* flags= */ 0), /* flags= */ 0))
+ .isEqualTo(effect);
+ }
+
+ private void assertHiddenApisRoundTrip(VibrationEffect effect) throws Exception {
+ String xml = serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS);
+ assertThat(parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS))
+ .isEqualTo(effect);
+ }
+
private TypedXmlPullParser createXmlPullParser(String xml) throws Exception {
TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
@@ -366,11 +523,6 @@
return parser;
}
- private void assertParseDocumentSucceeds(String xml, int flags, VibrationEffect... effects)
- throws Exception {
- assertThat(parseDocument(xml, flags).getVibrationEffects()).containsExactly(effects);
- }
-
/**
* Asserts parsing vibration from an open TypedXmlPullParser succeeds, and that the parser
* points to the end "vibration" or "vibration-select" tag.
@@ -385,7 +537,8 @@
String tagName = parser.getName();
assertThat(Set.of("vibration-effect", "vibration-select")).contains(tagName);
- assertThat(parseElement(parser, flags).getVibrationEffects()).containsExactly(effects);
+ assertThat(parseElement(parser, flags))
+ .isEqualTo(new ParsedVibration(Arrays.asList(effects)));
assertThat(parser.getEventType()).isEqualTo(XmlPullParser.END_TAG);
assertThat(parser.getName()).isEqualTo(tagName);
}
@@ -405,69 +558,40 @@
assertThat(parser.getEventType()).isEqualTo(parser.END_DOCUMENT);
}
- private void assertHiddenApisParseVibrationEffectSucceeds(String xml, VibrationEffect effect)
- throws IOException {
- assertThat(parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS))
- .isEqualTo(effect);
- }
-
- private void assertPublicApisSerializerFails(VibrationEffect effect) {
- assertThrows("Expected serialization to fail for " + effect,
- VibrationXmlSerializer.SerializationFailedException.class,
- () -> serialize(effect, /* flags= */ 0));
- }
-
private void assertParseElementFails(String xml) {
assertThrows("Expected parsing to fail for " + xml,
- VibrationXmlParser.VibrationXmlParserException.class,
+ VibrationXmlParser.ParseFailedException.class,
() -> parseElement(createXmlPullParser(xml), /* flags= */ 0));
}
- private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
- String... expectedSegments) throws IOException {
- assertSerializationContainsSegments(serialize(effect, /* flags= */ 0), expectedSegments);
- }
-
- private void assertHiddenApisSerializerSucceeds(VibrationEffect effect,
- String... expectedSegments) throws IOException {
- assertSerializationContainsSegments(
- serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS), expectedSegments);
- }
-
private void assertSerializationContainsSegments(String xml, String[] expectedSegments) {
for (String expectedSegment : expectedSegments) {
assertThat(xml).contains(expectedSegment);
}
}
- private void assertPublicApisRoundTrip(VibrationEffect effect) throws IOException {
- assertThat(parseVibrationEffect(serialize(effect, /* flags= */ 0), /* flags= */ 0))
- .isEqualTo(effect);
- }
-
- private void assertHiddenApisRoundTrip(VibrationEffect effect) throws IOException {
- String xml = serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS);
- assertThat(parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS))
- .isEqualTo(effect);
- }
-
private static VibrationEffect parseVibrationEffect(
- String xml, @VibrationXmlParser.Flags int flags) throws IOException {
+ String xml, @VibrationXmlParser.Flags int flags) throws Exception {
return VibrationXmlParser.parseVibrationEffect(new StringReader(xml), flags);
}
- private static ParsedVibration parseDocument(String xml, int flags)
- throws IOException {
+ private static ParsedVibration parseDocument(String xml, int flags) throws Exception {
return VibrationXmlParser.parseDocument(new StringReader(xml), flags);
}
private static ParsedVibration parseElement(TypedXmlPullParser parser, int flags)
- throws IOException, VibrationXmlParser.VibrationXmlParserException {
+ throws Exception {
return VibrationXmlParser.parseElement(parser, flags);
}
+ private static String serialize(VibrationEffect effect) throws Exception {
+ StringWriter writer = new StringWriter();
+ VibrationXmlSerializer.serialize(effect, writer);
+ return writer.toString();
+ }
+
private static String serialize(VibrationEffect effect, @VibrationXmlSerializer.Flags int flags)
- throws IOException {
+ throws Exception {
StringWriter writer = new StringWriter();
VibrationXmlSerializer.serialize(effect, writer, flags);
return writer.toString();
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index 7a4b6bc..99911de 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -69,5 +69,6 @@
<permission name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS" />
<permission name="android.permission.SATELLITE_COMMUNICATION" />
<permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
+ <permission name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/package-shareduid-allowlist.xml b/data/etc/package-shareduid-allowlist.xml
index 2401d4a..3b35f44 100644
--- a/data/etc/package-shareduid-allowlist.xml
+++ b/data/etc/package-shareduid-allowlist.xml
@@ -32,4 +32,5 @@
<config>
<allow-package-shareduid package="android.test.settings" shareduid="android.uid.system" />
+ <allow-package-shareduid package="com.android.performanceLaunch" shareduid="com.android.performanceapp.tests" />
</config>
diff --git a/data/keyboards/Vendor_18d1_Product_4f60.idc b/data/keyboards/Vendor_18d1_Product_4f60.idc
new file mode 100644
index 0000000..b9fd406
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_4f60.idc
@@ -0,0 +1,18 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Increase palm thresholds, since this touchpad has a tendency to overstate
+# touch sizes.
+gestureProp.Palm_Width = 40.0
+gestureProp.Multiple_Palm_Width = 40.0
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 3256f31..5caedba 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -416,7 +416,8 @@
}
/**
- * Retrieve the dataspace associated with the texture image.
+ * Retrieve the dataspace associated with the texture image
+ * set by the most recent call to {@link #updateTexImage}.
*/
@SuppressLint("MethodNameUnits")
public @NamedDataSpace int getDataSpace() {
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index c3aaf98..f8cb050 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -494,30 +494,37 @@
BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(),
getDataLength())));
case TYPE_URI:
- InputStream is = getUriInputStream(context);
- if (is != null) {
- final Bitmap bitmap = BitmapFactory.decodeStream(is);
- if (bitmap == null) {
- Log.w(TAG, "Unable to decode image from URI: " + getUriString());
- if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
- return null;
+ try (InputStream is = getUriInputStream(context)) {
+ if (is != null) {
+ final Bitmap bitmap = BitmapFactory.decodeStream(is);
+ if (bitmap == null) {
+ Log.w(TAG, "Unable to decode image from URI: " + getUriString());
+ if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
+ return null;
+ }
}
+ return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(bitmap));
}
- return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(bitmap));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
}
break;
case TYPE_URI_ADAPTIVE_BITMAP:
- is = getUriInputStream(context);
- if (is != null) {
- final Bitmap bitmap = BitmapFactory.decodeStream(is);
- if (bitmap == null) {
- Log.w(TAG, "Unable to decode image from URI: " + getUriString());
- if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
- return null;
+ try (InputStream is = getUriInputStream(context)) {
+ if (is != null) {
+ final Bitmap bitmap = BitmapFactory.decodeStream(is);
+ if (bitmap == null) {
+ Log.w(TAG, "Unable to decode image from URI: " + getUriString());
+ if (iconLoadDrawableReturnNullWhenUriDecodeFails()) {
+ return null;
+ }
}
+ return new AdaptiveIconDrawable(
+ null, new BitmapDrawable(context.getResources(),
+ fixMaxBitmapSize(bitmap)));
}
- return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(),
- fixMaxBitmapSize(bitmap)));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
}
break;
}
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 24aea37..ecf4eb4 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -17,7 +17,6 @@
package android.security;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -112,29 +111,6 @@
}
/**
- * Informs Keystore 2.0 about changing user's password
- *
- * @param userId - Android user id of the user
- * @param password - a secret derived from the synthetic password provided by the
- * LockSettingsService
- * @return 0 if successful or a {@code ResponseCode}
- * @hide
- */
- public static int onUserPasswordChanged(int userId, @Nullable byte[] password) {
- StrictMode.noteDiskWrite();
- try {
- getService().onUserPasswordChanged(userId, password);
- return 0;
- } catch (ServiceSpecificException e) {
- Log.e(TAG, "onUserPasswordChanged failed", e);
- return e.errorCode;
- } catch (Exception e) {
- Log.e(TAG, "Can not connect to keystore", e);
- return SYSTEM_ERROR;
- }
- }
-
- /**
* Tells Keystore that a user's LSKF is being removed, ie the user's lock screen is changing to
* Swipe or None. Keystore uses this notification to delete the user's auth-bound keys.
*
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 290fefa..822a07c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -169,6 +169,11 @@
@GuardedBy("mLock")
private int mDividerPosition;
+ /** Indicates if there are containers to be finished since the divider has appeared. */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ private boolean mHasContainersToFinish = false;
+
DividerPresenter(int taskId, @NonNull DragEventCallback dragEventCallback,
@NonNull Executor callbackExecutor) {
mTaskId = taskId;
@@ -180,7 +185,8 @@
void updateDivider(
@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentParentInfo parentInfo,
- @Nullable SplitContainer topSplitContainer) {
+ @Nullable SplitContainer topSplitContainer,
+ boolean isTaskFragmentVanished) {
if (!Flags.activityEmbeddingInteractiveDividerFlag()) {
return;
}
@@ -188,6 +194,18 @@
synchronized (mLock) {
// Clean up the decor surface if top SplitContainer is null.
if (topSplitContainer == null) {
+ // Check if there are containers to finish but the TaskFragment hasn't vanished yet.
+ // Don't remove the decor surface and divider if so as the removal should happen in
+ // a following step when the TaskFragment has vanished. This ensures that the decor
+ // surface is removed only after the resulting Activity is ready to be shown,
+ // otherwise there may be flicker.
+ if (mHasContainersToFinish) {
+ if (isTaskFragmentVanished) {
+ setHasContainersToFinish(false);
+ } else {
+ return;
+ }
+ }
removeDecorSurfaceAndDivider(wct);
return;
}
@@ -868,6 +886,12 @@
}
}
+ void setHasContainersToFinish(boolean hasContainersToFinish) {
+ synchronized (mLock) {
+ mHasContainersToFinish = hasContainersToFinish;
+ }
+ }
+
private static boolean isDraggingToFullscreenAllowed(
@NonNull DividerAttributes dividerAttributes) {
// TODO(b/293654166) Use DividerAttributes.isDraggingToFullscreenAllowed when extension is
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index f78e2b5..7ddda1f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -673,7 +673,7 @@
break;
case TYPE_TASK_FRAGMENT_VANISHED:
mPresenter.removeTaskFragmentInfo(info);
- onTaskFragmentVanished(wct, info);
+ onTaskFragmentVanished(wct, info, taskId);
break;
case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
onTaskFragmentParentInfoChanged(wct, taskId,
@@ -834,7 +834,7 @@
@VisibleForTesting
@GuardedBy("mLock")
void onTaskFragmentVanished(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentInfo taskFragmentInfo) {
+ @NonNull TaskFragmentInfo taskFragmentInfo, int taskId) {
final TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container != null) {
// Cleanup if the TaskFragment vanished is not requested by the organizer.
@@ -843,6 +843,11 @@
updateContainersInTaskIfVisible(wct, container.getTaskId());
}
cleanupTaskFragment(taskFragmentInfo.getFragmentToken());
+ final TaskContainer taskContainer = getTaskContainer(taskId);
+ if (taskContainer != null) {
+ // Update the divider to clean up any decor surfaces.
+ updateDivider(wct, taskContainer, true /* isTaskFragmentVanished */);
+ }
}
/**
@@ -884,7 +889,7 @@
// The divider need to be updated even if shouldUpdateContainer is false, because the decor
// surface may change in TaskFragmentParentInfo, which requires divider update but not
// container update.
- updateDivider(wct, taskContainer);
+ updateDivider(wct, taskContainer, false /* isTaskFragmentVanished */);
// If the last direct activity of the host task is dismissed and there's an always-on-top
// overlay container in the task, the overlay container should also be dismissed.
@@ -899,14 +904,23 @@
@GuardedBy("mLock")
void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) {
final TaskContainer taskContainer = getTaskContainer(taskId);
- if (taskContainer != null && taskContainer.isVisible()) {
+ if (taskContainer == null) {
+ return;
+ }
+
+ if (taskContainer.isVisible()) {
updateContainersInTask(wct, taskContainer);
+ } else if (Flags.fixNoContainerUpdateWithoutResize()) {
+ // the TaskFragmentContainers need to be updated when the task becomes visible
+ taskContainer.mTaskFragmentContainersNeedsUpdate = true;
}
}
@GuardedBy("mLock")
private void updateContainersInTask(@NonNull WindowContainerTransaction wct,
@NonNull TaskContainer taskContainer) {
+ taskContainer.mTaskFragmentContainersNeedsUpdate = false;
+
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
@@ -3257,12 +3271,15 @@
}
@GuardedBy("mLock")
- void updateDivider(
- @NonNull WindowContainerTransaction wct, @NonNull TaskContainer taskContainer) {
+ void updateDivider(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskContainer taskContainer, boolean isTaskFragmentVanished) {
final DividerPresenter dividerPresenter = mDividerPresenters.get(taskContainer.getTaskId());
final TaskFragmentParentInfo parentInfo = taskContainer.getTaskFragmentParentInfo();
- dividerPresenter.updateDivider(
- wct, parentInfo, taskContainer.getTopNonFinishingSplitContainer());
+ final SplitContainer topSplitContainer = taskContainer.getTopNonFinishingSplitContainer();
+ if (dividerPresenter != null) {
+ dividerPresenter.updateDivider(
+ wct, parentInfo, topSplitContainer, isTaskFragmentVanished);
+ }
}
@Override
@@ -3292,6 +3309,9 @@
final List<TaskFragmentContainer> containersToFinish = new ArrayList<>();
taskContainer.updateTopSplitContainerForDivider(
dividerPresenter, containersToFinish);
+ if (!containersToFinish.isEmpty()) {
+ dividerPresenter.setHasContainersToFinish(true);
+ }
for (final TaskFragmentContainer container : containersToFinish) {
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index d888fa9..d0e49d8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -374,7 +374,7 @@
updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
updateAnimationParams(wct, primaryContainer.getTaskFragmentToken(), splitAttributes);
updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
- mController.updateDivider(wct, taskContainer);
+ mController.updateDivider(wct, taskContainer, false /* isTaskFragmentVanished */);
}
private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
@@ -757,7 +757,8 @@
void expandTaskFragment(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
super.expandTaskFragment(wct, container);
- mController.updateDivider(wct, container.getTaskContainer());
+ mController.updateDivider(
+ wct, container.getTaskContainer(), false /* isTaskFragmentVanished */);
}
static boolean shouldShowSplit(@NonNull SplitContainer splitContainer) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index ee00c4c..20ad53e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -108,6 +108,12 @@
private boolean mPlaceholderRuleSuppressed;
/**
+ * {@code true} if the TaskFragments in this Task needs to be updated next time the Task
+ * becomes visible. See {@link #shouldUpdateContainer(TaskFragmentParentInfo)}
+ */
+ boolean mTaskFragmentContainersNeedsUpdate;
+
+ /**
* The {@link TaskContainer} constructor
*
* @param taskId The ID of the Task, which must match {@link Activity#getTaskId()} with
@@ -185,7 +191,8 @@
// If the task properties equals regardless of starting position, don't
// need to update the container.
- return mInfo.getConfiguration().diffPublicOnly(configuration) != 0
+ return mTaskFragmentContainersNeedsUpdate
+ || mInfo.getConfiguration().diffPublicOnly(configuration) != 0
|| mInfo.getDisplayId() != info.getDisplayId();
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 070fa5b..859bc2c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -17,6 +17,7 @@
package androidx.window.extensions.layout;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
@@ -41,6 +42,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiContext;
+import androidx.annotation.VisibleForTesting;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
@@ -138,6 +140,10 @@
throw new IllegalArgumentException("Context must be a UI Context, which should be"
+ " an Activity, WindowContext or InputMethodService");
}
+ if (context.getAssociatedDisplayId() == INVALID_DISPLAY) {
+ Log.w(TAG, "The registered Context is a UI Context but not associated with any"
+ + " display. This Context may not receive any WindowLayoutInfo update");
+ }
mFoldingFeatureProducer.getData((features) -> {
WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(context, features);
consumer.accept(newWindowLayout);
@@ -257,7 +263,8 @@
}
}
- private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) {
+ @VisibleForTesting
+ void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) {
synchronized (mLock) {
mLastReportedFoldingFeatures.clear();
mLastReportedFoldingFeatures.addAll(storedFeatures);
@@ -409,9 +416,10 @@
* @return true if the display features should be reported for the UI Context, false otherwise.
*/
private boolean shouldReportDisplayFeatures(@NonNull @UiContext Context context) {
- int displayId = context.getDisplay().getDisplayId();
+ int displayId = context.getAssociatedDisplayId();
if (displayId != DEFAULT_DISPLAY) {
- // Display features are not supported on secondary displays.
+ // Display features are not supported on secondary displays or the context is not
+ // associated with any display.
return false;
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index 4f51815..bc18cd2 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -82,6 +82,7 @@
*/
@Presubmit
@SmallTest
+@SuppressWarnings("GuardedBy")
@RunWith(AndroidJUnit4.class)
public class DividerPresenterTest {
@Rule
@@ -186,7 +187,8 @@
mDividerPresenter.updateDivider(
mTransaction,
mParentInfo,
- mSplitContainer);
+ mSplitContainer,
+ false /* isTaskFragmentVanished */);
assertNotEquals(mProperties, mDividerPresenter.mProperties);
verify(mRenderer).update();
@@ -206,7 +208,8 @@
mDividerPresenter.updateDivider(
mTransaction,
mParentInfo,
- mSplitContainer);
+ mSplitContainer,
+ false /* isTaskFragmentVanished */);
assertNotEquals(mProperties, mDividerPresenter.mProperties);
verify(mRenderer).update();
@@ -222,7 +225,8 @@
mDividerPresenter.updateDivider(
mTransaction,
mParentInfo,
- mSplitContainer);
+ mSplitContainer,
+ false /* isTaskFragmentVanished */);
assertEquals(mProperties, mDividerPresenter.mProperties);
verify(mRenderer, never()).update();
@@ -234,7 +238,42 @@
mDividerPresenter.updateDivider(
mTransaction,
mParentInfo,
- null /* splitContainer */);
+ null /* splitContainer */,
+ false /* isTaskFragmentVanished */);
+ final TaskFragmentOperation taskFragmentOperation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE)
+ .build();
+
+ verify(mTransaction).addTaskFragmentOperation(
+ mPrimaryContainerToken, taskFragmentOperation);
+ verify(mRenderer).release();
+ assertNull(mDividerPresenter.mRenderer);
+ assertNull(mDividerPresenter.mProperties);
+ assertNull(mDividerPresenter.mDecorSurfaceOwner);
+ }
+
+ @Test
+ public void testUpdateDivider_noChangeWhenHasContainersToFinishButTaskFragmentNotVanished() {
+ mDividerPresenter.setHasContainersToFinish(true);
+ mDividerPresenter.updateDivider(
+ mTransaction,
+ mParentInfo,
+ null /* splitContainer */,
+ false /* isTaskFragmentVanished */);
+
+ assertEquals(mProperties, mDividerPresenter.mProperties);
+ verify(mRenderer, never()).update();
+ verify(mTransaction, never()).addTaskFragmentOperation(any(), any());
+ }
+
+ @Test
+ public void testUpdateDivider_dividerRemovedWhenHasContainersToFinishAndTaskFragmentVanished() {
+ mDividerPresenter.setHasContainersToFinish(true);
+ mDividerPresenter.updateDivider(
+ mTransaction,
+ mParentInfo,
+ null /* splitContainer */,
+ true /* isTaskFragmentVanished */);
final TaskFragmentOperation taskFragmentOperation = new TaskFragmentOperation.Builder(
OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE)
.build();
@@ -254,7 +293,8 @@
mDividerPresenter.updateDivider(
mTransaction,
mParentInfo,
- mSplitContainer);
+ mSplitContainer,
+ false /* isTaskFragmentVanished */);
final TaskFragmentOperation taskFragmentOperation = new TaskFragmentOperation.Builder(
OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE)
.build();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 640b1fc..efeec82 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -200,12 +200,14 @@
public void testOnTaskFragmentVanished() {
final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
doReturn(tf.getTaskFragmentToken()).when(mInfo).getFragmentToken();
+ doReturn(createTestTaskContainer()).when(mSplitController).getTaskContainer(TASK_ID);
// The TaskFragment has been removed in the server, we only need to cleanup the reference.
- mSplitController.onTaskFragmentVanished(mTransaction, mInfo);
+ mSplitController.onTaskFragmentVanished(mTransaction, mInfo, TASK_ID);
verify(mSplitPresenter, never()).deleteTaskFragment(any(), any());
verify(mSplitController).removeContainer(tf);
+ verify(mSplitController).updateDivider(any(), any(), anyBoolean());
verify(mTransaction, never()).finishActivity(any());
}
@@ -1152,7 +1154,7 @@
.setTaskFragmentInfo(info));
mSplitController.onTransactionReady(transaction);
- verify(mSplitController).onTaskFragmentVanished(any(), eq(info));
+ verify(mSplitController).onTaskFragmentVanished(any(), eq(info), anyInt());
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
new file mode 100644
index 0000000..ff0a82f
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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 androidx.window.extensions.layout;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+
+/**
+ * Test class for {@link WindowLayoutComponentImpl}.
+ *
+ * Build/Install/Run:
+ * atest WMJetpackUnitTests:WindowLayoutComponentImplTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WindowLayoutComponentImplTest {
+
+ private WindowLayoutComponentImpl mWindowLayoutComponent;
+
+ @Before
+ public void setUp() {
+ mWindowLayoutComponent = new WindowLayoutComponentImpl(
+ ApplicationProvider.getApplicationContext(),
+ mock(DeviceStateManagerFoldingFeatureProducer.class));
+ }
+
+ @Test
+ public void testAddWindowLayoutListenerOnFakeUiContext_noCrash() {
+ final Context fakeUiContext = createTestContext();
+
+ mWindowLayoutComponent.addWindowLayoutInfoListener(fakeUiContext, info -> {});
+
+ mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList());
+ }
+
+ private static Context createTestContext() {
+ return new FakeUiContext(ApplicationProvider.getApplicationContext());
+ }
+
+ /**
+ * A {@link android.content.Context} overrides {@link android.content.Context#isUiContext} to
+ * {@code true}.
+ */
+ private static class FakeUiContext extends ContextWrapper {
+
+ FakeUiContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public boolean isUiContext() {
+ return true;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index bcb1d29..52ae93f 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -29,6 +29,7 @@
android:name=".desktopmode.DesktopWallpaperActivity"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
+ android:showForAllUsers="true"
android:theme="@style/DesktopWallpaperTheme" />
<activity
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 112eb61..3ff40e0 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -121,3 +121,20 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_taskbar_on_phones"
+ namespace: "multitasking"
+ description: "Enables taskbar on phones"
+ bug: "348007377"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_bubble_bar_in_persistent_task_bar"
+ namespace: "multitasking"
+ description: "Enable bubble bar to be shown in the persistent task bar"
+ bug: "346391377"
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
new file mode 100644
index 0000000..c6dbd9b
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
@@ -0,0 +1,61 @@
+// 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 {
+ // 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"],
+ default_team: "trendy_team_multitasking_windowing",
+}
+
+android_test {
+ name: "WMShellMultivalentScreenshotTestsOnDevice",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "WindowManager-Shell",
+ "junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "truth",
+ "platform-parametric-runner-lib",
+ "platform-screenshot-diff-core",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: [
+ "--extra-packages",
+ "com.android.wm.shell",
+ ],
+ manifest: "AndroidManifest.xml",
+ asset_dirs: ["goldens/onDevice"],
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifest.xml
new file mode 100644
index 0000000..467dc6a
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.wm.shell.multivalentscreenshot">
+
+ <application android:debuggable="true" android:supportsRtl="true" >
+ <uses-library android:name="android.test.runner" />
+ <activity
+ android:name="platform.test.screenshot.ScreenshotActivity"
+ android:exported="true">
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Multivalent screenshot tests for WindowManager-Shell"
+ android:targetPackage="com.android.wm.shell.multivalentscreenshot">
+ </instrumentation>
+
+ <!-- this permission is required by Tuner Service in screenshot tests -->
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+</manifest>
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
new file mode 100644
index 0000000..a7a3f13
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
@@ -0,0 +1,25 @@
+<?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.wm.shell.multivalentscreenshot">
+ <application android:debuggable="true" android:supportsRtl="true">
+ <uses-library android:name="android.test.runner" />
+ <activity
+ android:name="platform.test.screenshot.ScreenshotActivity"
+ android:exported="true">
+ </activity>
+ </application>
+</manifest>
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidTest.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidTest.xml
new file mode 100644
index 0000000..75793ae
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?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 Tests for WindowManagerShellLib">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="WMShellMultivalentScreenshotTestsOnDevice.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="WMShellMultivalentScreenshotTestsOnDevice" />
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/user/0/com.android.wm.shell.multivalentscreenshot/files/wmshell_screenshots" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.wm.shell.multivalentscreenshot" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
new file mode 100644
index 0000000..eb28881
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
new file mode 100644
index 0000000..eb28881
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties
new file mode 100644
index 0000000..7a0527c
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties
@@ -0,0 +1,2 @@
+sdk=NEWEST_SDK
+
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
new file mode 100644
index 0000000..d35f493
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles
+
+import android.view.LayoutInflater
+import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager
+import com.android.wm.shell.R
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.Displays
+import platform.test.screenshot.ViewScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+@RunWith(ParameterizedAndroidJunit4::class)
+class BubbleEducationViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.forDisplays(Displays.Phone, isLandscape = false)
+ }
+
+ @get:Rule
+ val screenshotRule =
+ ViewScreenshotTestRule(
+ emulationSpec,
+ WMShellGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec))
+ )
+
+ @Test
+ fun bubblesEducation() {
+ screenshotRule.screenshotTest("bubbles_education") { activity ->
+ activity.actionBar?.hide()
+ val view =
+ LayoutInflater.from(activity)
+ .inflate(R.layout.bubble_bar_stack_education, null) as BubblePopupView
+ view.setup()
+ view
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/testing/goldenpathmanager/WMShellGoldenPathManager.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/testing/goldenpathmanager/WMShellGoldenPathManager.kt
new file mode 100644
index 0000000..901b79b
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/testing/goldenpathmanager/WMShellGoldenPathManager.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.testing.goldenpathmanager
+
+import android.os.Build
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenPathManager
+import platform.test.screenshot.PathConfig
+
+/** A WM Shell specific implementation of [GoldenPathManager]. */
+class WMShellGoldenPathManager(pathConfig: PathConfig) :
+ GoldenPathManager(
+ appContext = InstrumentationRegistry.getInstrumentation().context,
+ assetsPathRelativeToBuildRoot = assetPath,
+ deviceLocalPath = deviceLocalPath,
+ pathConfig = pathConfig,
+ ) {
+
+ private companion object {
+ private const val ASSETS_PATH =
+ "frameworks/base/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice"
+ private const val ASSETS_PATH_ROBO =
+ "frameworks/base/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/" +
+ "robolectric"
+ private val assetPath: String
+ get() = if (Build.FINGERPRINT.contains("robolectric")) ASSETS_PATH_ROBO else ASSETS_PATH
+ private val deviceLocalPath: String
+ get() =
+ InstrumentationRegistry.getInstrumentation()
+ .targetContext
+ .filesDir
+ .absolutePath
+ .toString() + "/wmshell_screenshots"
+ }
+ override fun toString(): String {
+ // This string is appended to all actual/expected screenshots on the device, so make sure
+ // it is a static value.
+ return "WMShellGoldenPathManager"
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTestsForDevice b/libs/WindowManager/Shell/multivalentScreenshotTestsForDevice
new file mode 120000
index 0000000..e879efc
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTestsForDevice
@@ -0,0 +1 @@
+multivalentScreenshotTests
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
index 34f03c2..501bedd 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
@@ -19,7 +19,7 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
- android:id="@+id/bubble_bar_expanded_view">
+ android:id="@+id/bubble_expanded_view">
<com.android.wm.shell.bubbles.bar.BubbleBarHandleView
android:id="@+id/bubble_bar_handle_view"
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 1c8f5e6..8b328e2 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tik om hierdie app te herbegin vir ’n beter aansig"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Verander hierdie app se aspekverhouding in Instellings"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Verander aspekverhouding"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 81ab3ab..b005a01 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"ለተሻለ ዕይታ ይህን መተግበሪያ እንደገና ለመጀመር መታ ያድርጉ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"የዚህን መተግበሪያ ምጥጥነ ገፅታ በቅንብሮች ውስጥ ይለውጡ"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"የምጥጥነ ገፅታ ለውጥ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 3974c39..8c283d3 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"انقر لإعادة تشغيل هذا التطبيق للحصول على تجربة عرض أفضل."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"يمكنك تغيير نسبة العرض إلى الارتفاع لهذا التطبيق من خلال \"الإعدادات\"."</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغيير نسبة العرض إلى الارتفاع"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index a1ce1b3..ef92587 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"উন্নত ভিউ পোৱাৰ বাবে এপ্টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ছেটিঙলৈ গৈ এই এপ্টোৰ আকাৰৰ অনুপাত সলনি কৰক"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"আকাৰৰ অনুপাত সলনি কৰক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 71dfe5a..04b2f1c 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Yaxşı görünüş üçün toxunaraq bu tətbiqi yenidən başladın"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ayarlarda bu tətbiqin tərəflər nisbətini dəyişin"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tərəflər nisbətini dəyişin"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index f483609..47bc105 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promenite razmeru ove aplikacije u Podešavanjima"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promeni razmeru"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 532ecc6..6ad7553 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Націсніце, каб перазапусціць гэту праграму для зручнейшага прагляду"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змяніць суадносіны бакоў для гэтай праграмы ў наладах"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змяніць суадносіны бакоў"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 8f828ba..a9e0bce 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Докоснете, за да рестартирате това приложение с цел по-добър изглед"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Променете съотношението на това приложение в „Настройки“"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промяна на съотношението"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index e0a2ea8..29de100 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"সেটিংস থেকে এই অ্যাপের অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 41c72c1..5f1da75 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijenite format slike aplikacije u Postavkama"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijenite format slike"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 6792272..d70de79 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Canvia la relació d\'aspecte d\'aquesta aplicació a Configuració"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Canvia la relació d\'aspecte"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 150a6e6..ca00fec 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci restartujete kvůli lepšímu zobrazení"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 8878910..d50d2f0 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tryk for at genstarte denne app, så visningen forbedres"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Skift denne apps billedformat i Indstillinger"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Skift billedformat"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 7b91559..7f44f83 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tippen, um diese App neu zu starten und die Ansicht zu verbessern"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Seitenverhältnis der App in den Einstellungen ändern"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Seitenverhältnis ändern"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 14e5e2f..a3a5ccd 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Αλλάξτε τον λόγο διαστάσεων αυτής της εφαρμογής στις Ρυθμίσεις"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Αλλαγή λόγου διαστάσεων"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 7427b62..edc4f4e 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index cb9ee4f..d7e23fd 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -84,6 +84,8 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="bubble_shortcut_label" msgid="666269077944378311">"Bubbles"</string>
+ <string name="bubble_shortcut_long_label" msgid="6088437544312894043">"Show Bubbles"</string>
<string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 7427b62..edc4f4e 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 7427b62..edc4f4e 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 8498807..1da8c27 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -84,6 +84,8 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+ <string name="bubble_shortcut_label" msgid="666269077944378311">"Bubbles"</string>
+ <string name="bubble_shortcut_long_label" msgid="6088437544312894043">"Show Bubbles"</string>
<string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 406c1f3..8653e59 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Presiona para reiniciar esta app y tener una mejor vista"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta app en Configuración"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 0583d79..8f59c9c 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Toca para reiniciar esta aplicación y obtener una mejor vista"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta aplicación en Ajustes"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 70547f5..3d86eb4 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muutke selle rakenduse kuvasuhet jaotises Seaded"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Muutke kuvasuhet"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 4be35ea..4e7bdd2 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Hobeto ikusteko, sakatu hau, eta aplikazioa berrabiarazi egingo da"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Aldatu aplikazioaren aspektu-erlazioa ezarpenetan"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Aldatu aspektu-erlazioa"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 32d5f5f..4b9be47 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -53,7 +53,7 @@
<string name="accessibility_split_top" msgid="2789329702027147146">"تقسیم از بالا"</string>
<string name="accessibility_split_bottom" msgid="8694551025220868191">"تقسیم از پایین"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از حالت یکدستی"</string>
- <string name="one_handed_tutorial_description" msgid="3486582858591353067">"برای خارج شدن، از پایین صفحهنمایش تند بهطرف بالا بکشید یا در هر جایی از بالای برنامه که میخواهید ضربه بزنید"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"برای خارج شدن، از پایین صفحهنمایش تند بهطرف بالا بکشید یا در هر جایی از بالای برنامه که میخواهید تکضرب بزنید"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"آغاز «حالت یکدستی»"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"خروج از «حالت یکدستی»"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"تنظیمات برای حبابکهای <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -71,25 +71,29 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"رد کردن حبابک"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"مکالمه در حباب نشان داده نشود"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابکها"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمههای جدید بهصورت نمادهای شناور یا حبابکها نشان داده میشوند. برای باز کردن حبابکها ضربه بزنید. برای جابهجایی، آن را بکشید."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمههای جدید بهصورت نمادهای شناور یا حبابکها نشان داده میشوند. برای باز کردن حبابکها تکضرب بزنید. برای جابهجایی، آن را بکشید."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابکها در هرزمانی"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابکها از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابکها از این برنامه، روی «مدیریت» تکضرب بزنید"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجهام"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکهای اخیر و حبابکهای ردشده اینجا ظاهر خواهند شد"</string>
<string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"گپ زدن بااستفاده از حبابک"</string>
- <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"مکالمههای جدید بهصورت نماد در گوشه پایین صفحهنمایش نشان داده میشود. برای ازهم بازکردن آنها ضربه بزنید یا برای بستن، آنها را بکشید."</string>
+ <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"مکالمههای جدید بهصورت نماد در گوشه پایین صفحهنمایش نشان داده میشود. برای ازهم بازکردن آنها تکضرب بزنید یا برای بستن، آنها را بکشید."</string>
<string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"کنترل حبابکها در هرزمانی"</string>
- <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"برای مدیریت اینکه کدام برنامهها و مکالمهها حباب داشته باشند، ضربه بزنید"</string>
+ <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"برای مدیریت اینکه کدام برنامهها و مکالمهها حباب داشته باشند، تکضرب بزنید"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
- <string name="restart_button_description" msgid="4564728020654658478">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراهاندازی شود"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
+ <string name="restart_button_description" msgid="4564728020654658478">"برای داشتن نمایی بهتر، تکضرب بزنید تا این برنامه بازراهاندازی شود"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"نسبت ابعادی این برنامه را در «تنظیمات» تغییر دهید"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغییر نسبت ابعادی"</string>
- <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
- <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
- <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
+ <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه تکضرب بزنید"</string>
+ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن تکضرب بزنید"</string>
+ <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن تکضرب بزنید."</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه بهطور همزمان استفاده کنید"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامهای دیگر بکشید"</string>
<string name="letterbox_education_reposition_text" msgid="4589957299813220661">"برای جابهجا کردن برنامه، بیرون از آن دوضربه بزنید"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 6f03545..577d625 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muuta tämän sovelluksen kuvasuhdetta Asetuksissa"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Vaihda kuvasuhdetta"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 3492f13..74d822a 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Changer les proportions de cette application dans les paramètres"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier les proportions"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 4002e4d..4d14d0b 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Appuyez pour redémarrer cette appli et obtenir une meilleure vue."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Modifiez le format de cette appli dans les Paramètres."</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier le format"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index c371f7f..e5b67c2 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia a proporción desta aplicación en Configuración"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar a proporción"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 7e3d7a3..e2a52dc 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"સેટિંગમાં આ ઍપનો સાપેક્ષ ગુણોત્તર બદલો"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"સાપેક્ષ ગુણોત્તર બદલો"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index cd0f4e3..f75e0e0 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"बेहतर व्यू पाने के लिए, टैप करके ऐप्लिकेशन को रीस्टार्ट करें"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिंग में जाकर इस ऐप्लिकेशन का आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 27d4cfc..ed80c50 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijeni omjer slike ove aplikacije u postavkama"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijeni omjer slike"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index a8cc5c1..32a3106 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"A jobb nézet érdekében koppintson az alkalmazás újraindításához."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Az app méretarányát a Beállításokban módosíthatja"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Méretarány módosítása"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 7f37277..65ca704 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Փոխել հավելվածի կողմերի հարաբերակցությունը Կարգավորումներում"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Փոխել չափերի հարաբերակցությունը"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 3cf55fa..975dd72 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ubah rasio aspek aplikasi ini di Setelan"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ubah rasio aspek"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 6aa56f9..11c4718 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Ýttu til að endurræsa forritið og fá betri sýn"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Breyta myndhlutfalli þessa forrits í stillingunum"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Breyta myndhlutfalli"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 3c1d5e4..168c8cc 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tocca per riavviare l\'app e migliorare la visualizzazione"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia le proporzioni dell\'app nelle Impostazioni"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambia proporzioni"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index a0c3b3a..fd4cd1a 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"אפשר לשנות את יחס הגובה-רוחב של האפליקציה הזו ב\'הגדרות\'"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"שינוי יחס גובה-רוחב"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fb726c1..64ddec9 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"タップしてこのアプリを再起動すると、表示が適切になります"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"このアプリのアスペクト比を [設定] で変更します"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"アスペクト比を変更"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index e9f620a..cab8807 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"შეხებით გადატვირთეთ ეს აპი უკეთესი ხედის მისაღებად"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"შეცვალეთ ამ აპის თანაფარდობა პარამეტრებიდან"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"თანაფარდობის შეცვლა"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 34e4103..4ff5b85 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Көріністі жақсарту үшін осы қолданбаны түртіп, қайта ашыңыз."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Осы қолданбаның арақатынасын параметрлерден өзгертуге болады."</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Арақатынасты өзгерту"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 362bbad..ba7a324 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោលសារលេចឡើង។"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"ចុចដើម្បីចាប់ផ្ដើមកម្មវិធីនេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ផ្លាស់ប្ដូរសមាមាត្ររបស់កម្មវិធីនេះនៅក្នុងការកំណត់"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ប្ដូរសមាមាត្រ"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 77cc4a4..423e8d5 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಈ ಆ್ಯಪ್ನ ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index caa114f..0d1c621 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"탭하면 앱을 다시 시작하여 보기를 개선합니다."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"설정에서 앱의 가로세로 비율을 변경합니다."</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"가로세로 비율 변경"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 302c007..f17e9ca 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Бул колдонмонун тараптарынын катнашын параметрлерден өзгөртүү"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Тараптардын катнашын өзгөртүү"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a351963..195e4d5 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ປ່ຽນອັດຕາສ່ວນຂອງແອັບນີ້ໃນການຕັ້ງຄ່າ"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ປ່ຽນອັດຕາສ່ວນ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index e4dd739..63ad580 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Pakeiskite šios programos kraštinių santykį"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Keisti kraštinių santykį"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 99aebf6..268d893 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Iestatījumos mainiet šīs lietotnes malu attiecību."</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mainīt malu attiecību"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index c152c60..0a0027f 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Допрете за да ја рестартирате апликацијава за подобар приказ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промени го соодносот на апликацијава во „Поставки“"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Променување на соодносот"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 90275cd..07809e1 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"മികച്ച കാഴ്ചയ്ക്കായി ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ഈ ആപ്പിന്റെ വീക്ഷണ അനുപാതം, ക്രമീകരണത്തിൽ മാറ്റുക"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 5e43506..99bd2df 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Энэ аппын харьцааг Тохиргоонд өөрчилнө үү"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Харьцааг өөрчлөх"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 5874bff..ac57e0a5 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"अधिक चांगल्या दृश्यासाठी हे अॅप रीस्टार्ट करण्याकरिता टॅप करा"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिंग्ज मध्ये या ॲपचा आस्पेक्ट रेशो बदला"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"आस्पेक्ट रेशो बदला"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 4de8a7b..6bc2fbb 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Tukar nisbah bidang apl ini dalam Tetapan"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tukar nisbah bidang"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 5b9e9cb..12c19ed 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့ပါ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ဆက်တင်များတွင် ဤအက်ပ်၏အချိုးအစားကို ပြောင်းရန်"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"အချိုးစား ပြောင်းရန်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 6005be4..1161eb6 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i Innstillinger"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index a5bd2ab..25d0337 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"अझ राम्रो भ्यू प्राप्त गर्नका लागि यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस्"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिङमा गई यो एपको एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 0cd27c5..4ad343c 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tik om deze app opnieuw op te starten voor een betere weergave"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Wijzig de beeldverhouding van deze app in Instellingen"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Beeldverhouding wijzigen"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index bf75185..966d404 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ସେଟିଂସରେ ଏହି ଆପର ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 325c1e8..9feaf41 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਵਾਸਤੇ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਇਸ ਐਪ ਦੇ ਆਕਾਰ ਅਨੁਪਾਤ ਨੂੰ ਬਦਲੋ"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index a7648c8..1c7fbf8 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Kliknij w celu zrestartowania aplikacji, aby lepiej się wyświetlała."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmień proporcje obrazu aplikacji w Ustawieniach"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmień proporcje obrazu"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index e47d151..5c2de2a 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 1210fe8..6f76525 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar esta app e ficar com uma melhor visão"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Altere o formato desta app nas Definições"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Altere o formato"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index e47d151..5c2de2a 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index ae871f3..6e85e78 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionează"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Atinge ca să repornești aplicația pentru o vizualizare mai bună"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Schimbă raportul de dimensiuni al aplicației din Setări"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Schimbă raportul de dimensiuni"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 971e146..1b41983 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Нажмите, чтобы перезапустить приложение и оптимизировать размер"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Изменить соотношение сторон приложения в настройках"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Изменить соотношение сторон"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index ef1381c..6fd37e9 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"වඩා හොඳ දසුනක් සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"සැකසීම් තුළ මෙම යෙදුමේ දර්ශන අනුපාතය වෙනස් කරන්න"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 55a0312..dabbf39 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmeniť pomer strán tejto aplikácie v Nastaveniach"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmeniť pomer strán"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index bb123dc..3ade338 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Razmerje stranic te aplikacije spremenite v nastavitvah."</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Sprememba razmerja stranic"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index c74a8cd..ee1aa00 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Trokit për ta rinisur këtë aplikacion për një pamje më të mirë"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ndrysho raportin e pamjes së këtij aplikacioni te \"Cilësimet\""</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ndrysho raportin e pamjes"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 0694a97..b2868ca 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Додирните да бисте рестартовали ову апликацију ради бољег приказа"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промените размеру ове апликације у Подешавањима"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промени размеру"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 8e0bcfe..66118ef 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Tryck för att starta om appen och få en bättre vy"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ändra appens bildformat i inställningarna"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ändra bildformat"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 41180ab..863b49b 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Badilisha uwiano wa programu hii katika Mipangilio"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Badilisha uwiano wa kipengele"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 01ac78d..74e0207 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"இங்கு தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்டப்படும் விதத்தை இன்னும் சிறப்பாக்கலாம்"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"அமைப்புகளில் இந்த ஆப்ஸின் தோற்ற விகிதத்தை மாற்றும்"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"தோற்ற விகிதத்தை மாற்றும்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 6224e72..3571156 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"మెరుగైన వీక్షణ కోసం ఈ యాప్ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"సెట్టింగ్లలో ఈ యాప్ ఆకార నిష్పత్తిని మార్చండి"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ఆకార నిష్పత్తిని మార్చండి"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index fe0b74c..4769416 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"เปลี่ยนสัดส่วนภาพของแอปนี้ในการตั้งค่า"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"เปลี่ยนอัตราส่วนกว้างยาว"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 786e99c..be18d88 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"I-tap para i-restart ang app na ito para sa mas magandang view"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Baguhin ang aspect ratio ng app na ito sa Mga Setting"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Baguhin ang aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index e953f58..4c8c536 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Bu uygulamayı yeniden başlatarak daha iyi bir görünüm elde etmek için dokunun"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Bu uygulamanın en boy oranını Ayarlar\'dan değiştirin"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"En boy oranını değiştir"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index fbdf42e..7cc1a04 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змінити формат для цього додатка в налаштуваннях"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змінити формат"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 5562fa7..8b9f299 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ترتیبات میں اس ایپ کی تناسبی شرح کو تبدیل کریں"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تناسبی شرح کو تبدیل کریں"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 50e4232..55c6b32 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Sozlamalar orqali bu ilovaning tomonlar nisbatini oʻzgartiring"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tomonlar nisbatini oʻzgartirish"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 6da8588..07a6b6f 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Nhấn nút khởi động lại ứng dụng này để xem dễ hơn"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Thay đổi tỷ lệ khung hình của ứng dụng này thông qua phần Cài đặt"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Thay đổi tỷ lệ khung hình"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 4318caf..908095a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭消息气泡。"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"点按即可重启此应用,获得更好的视觉体验"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"在“设置”中更改此应用的宽高比"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"更改高宽比"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 72cd39d..c8550b4 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更此應用程式的長寬比"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更長寬比"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index c06d7b1..6704833 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"輕觸此按鈕重新啟動這個應用程式,即可獲得更良好的觀看體驗"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更這個應用程式的顯示比例"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更顯示比例"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 755414e..96b4faec 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -84,6 +84,10 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+ <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+ <skip />
+ <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+ <skip />
<string name="restart_button_description" msgid="4564728020654658478">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Shintsha ukubukeka kwesilinganiselo kwe-app kuMasethingi"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Shintsha ukubukeka kwesilinganiselo"</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index a3111b3..c9d3dbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -324,6 +324,7 @@
enteringHasSameLetterbox = false
lastPostCommitFlingScale = SPRING_SCALE
gestureProgress = 0f
+ triggerBack = false
}
protected fun applyTransform(
@@ -499,10 +500,12 @@
}
override fun onBackCancelled() {
+ triggerBack = false
progressAnimator.onBackCancelled { finishAnimation() }
}
override fun onBackInvoked() {
+ triggerBack = true
progressAnimator.reset()
onGestureCommitted(progressAnimator.velocity)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 1279fc4..2aefc64 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -894,11 +894,22 @@
}
@Nullable
- Intent getAppBubbleIntent() {
+ @VisibleForTesting
+ public Intent getAppBubbleIntent() {
return mAppIntent;
}
/**
+ * Sets the intent for a bubble that is an app bubble (one for which {@link #mIsAppBubble} is
+ * true).
+ *
+ * @param appIntent The intent to set for the app bubble.
+ */
+ void setAppBubbleIntent(Intent appIntent) {
+ mAppIntent = appIntent;
+ }
+
+ /**
* Returns whether this bubble is from an app versus a notification.
*/
public boolean isAppBubble() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d2c36e6..c853301 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1450,6 +1450,8 @@
if (b != null) {
// It's in the overflow, so remove it & reinflate
mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_NOTIF_CANCEL);
+ // Update the bubble entry in the overflow with the latest intent.
+ b.setAppBubbleIntent(intent);
} else {
// App bubble does not exist, lets add and expand it
b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 6ffeb97..58007b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -27,7 +27,9 @@
import android.util.Size;
import android.view.Gravity;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -39,6 +41,9 @@
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
+ // The same value (with the same name) is used in Launcher.
+ private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.01f;
+
@NonNull private final PipBoundsState mPipBoundsState;
@NonNull protected final PipDisplayLayoutState mPipDisplayLayoutState;
@NonNull protected final SizeSpecSource mSizeSpecSource;
@@ -206,9 +211,27 @@
*/
public static boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint,
Rect destinationBounds) {
- return sourceRectHint != null
- && sourceRectHint.width() > destinationBounds.width()
- && sourceRectHint.height() > destinationBounds.height();
+ if (sourceRectHint == null || sourceRectHint.isEmpty()) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "isSourceRectHintValidForEnterPip=false, empty hint");
+ return false;
+ }
+ if (sourceRectHint.width() <= destinationBounds.width()
+ || sourceRectHint.height() <= destinationBounds.height()) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "isSourceRectHintValidForEnterPip=false, hint(%s) is smaller"
+ + " than destination(%s)", sourceRectHint, destinationBounds);
+ return false;
+ }
+ final float reportedRatio = destinationBounds.width() / (float) destinationBounds.height();
+ final float inferredRatio = sourceRectHint.width() / (float) sourceRectHint.height();
+ if (Math.abs(reportedRatio - inferredRatio) > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "isSourceRectHintValidForEnterPip=false, hint(%s) does not match"
+ + " destination(%s) aspect ratio", sourceRectHint, destinationBounds);
+ return false;
+ }
+ return true;
}
public float getDefaultAspectRatio() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index a09720d..3e9366f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -34,6 +34,7 @@
import com.android.wm.shell.Flags
import com.android.wm.shell.protolog.ShellProtoLogGroup
import kotlin.math.abs
+import kotlin.math.roundToInt
/** A class that includes convenience methods. */
object PipUtils {
@@ -149,16 +150,16 @@
val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height()
val width: Int
val height: Int
- var left = 0
- var top = 0
+ var left = appBounds.left
+ var top = appBounds.top
if (appBoundsAspRatio < aspectRatio) {
width = appBounds.width()
- height = Math.round(width / aspectRatio)
- top = (appBounds.height() - height) / 2
+ height = (width / aspectRatio).roundToInt()
+ top = appBounds.top + (appBounds.height() - height) / 2
} else {
height = appBounds.height()
- width = Math.round(height * aspectRatio)
- left = (appBounds.width() - width) / 2
+ width = (height * aspectRatio).roundToInt()
+ left = appBounds.left + (appBounds.width() - width) / 2
}
return Rect(left, top, left + width, top + height)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index c2242a8..2234041 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -336,11 +336,6 @@
setTouching();
mStartPos = touchPos;
mMoving = false;
- // This triggers initialization of things like the resize veil in preparation for
- // showing it when the user moves the divider past the slop, and has to be done
- // before onStartDragging() which starts the jank interaction tracing
- mSplitLayout.updateDividerBounds(mSplitLayout.getDividerPosition(),
- false /* shouldUseParallaxEffect */);
mSplitLayout.onStartDragging();
break;
case MotionEvent.ACTION_MOVE:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index bfac24b..2520c25 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -258,9 +258,15 @@
return;
}
// We're showing the first reachability education so we ignore incoming TaskInfo
- // until the education flow has completed or we double tap.
+ // until the education flow has completed or we double tap. The double-tap
+ // basically cancel all the onboarding flow. We don't have to ignore events in case
+ // the app is in size compat mode.
if (mIsFirstReachabilityEducationRunning) {
- return;
+ if (!taskInfo.appCompatTaskInfo.isFromLetterboxDoubleTap
+ && !taskInfo.appCompatTaskInfo.topActivityInSizeCompat) {
+ return;
+ }
+ mIsFirstReachabilityEducationRunning = false;
}
if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
if (taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled) {
@@ -278,17 +284,24 @@
final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
&& !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(taskInfo);
if (isFirstTimeHorizontalReachabilityEdu || isFirstTimeVerticalReachabilityEdu) {
- mIsFirstReachabilityEducationRunning = true;
mCompatUIConfiguration.setSeenLetterboxEducation(taskInfo.userId);
- createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
- return;
+ // We activate the first reachability education if the double-tap is enabled.
+ // If the double tap is not enabled (e.g. thin letterbox) we just set the value
+ // of the education being seen.
+ if (taskInfo.appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+ mIsFirstReachabilityEducationRunning = true;
+ createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ return;
+ }
}
}
}
createOrUpdateCompatLayout(taskInfo, taskListener);
createOrUpdateRestartDialogLayout(taskInfo, taskListener);
if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) {
- createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ if (taskInfo.appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+ createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ }
// The user aspect ratio button should not be handled when a new TaskInfo is
// sent because of a double tap or when in multi-window mode.
if (taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 87bd840..43cdcca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -17,12 +17,14 @@
package com.android.wm.shell.dagger;
import android.annotation.Nullable;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
import android.os.UserManager;
import android.view.Choreographer;
import android.view.IWindowManager;
+import android.view.SurfaceControl;
import android.view.WindowManager;
import com.android.internal.jank.InteractionJankMonitor;
@@ -400,7 +402,8 @@
Optional<RecentTasksController> recentTasksController,
HomeTransitionObserver homeTransitionObserver) {
return new RecentsTransitionHandler(shellInit, transitions,
- recentTasksController.orElse(null), homeTransitionObserver);
+ recentTasksController.orElse(null), homeTransitionObserver,
+ SurfaceControl.Transaction::new);
}
//
@@ -512,6 +515,7 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
DragAndDropController dragAndDropController,
Transitions transitions,
+ KeyguardManager keyguardManager,
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
@@ -526,7 +530,7 @@
Optional<RecentTasksController> recentTasksController) {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
- dragAndDropController, transitions, enterDesktopTransitionHandler,
+ dragAndDropController, transitions, keyguardManager, enterDesktopTransitionHandler,
exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
dragToDesktopTransitionHandler, desktopModeTaskRepository,
desktopModeLoggerTransitionObserver, launchAdjacentController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 9192e6e..fbc11c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -159,13 +159,24 @@
}
companion object {
+ /**
+ * Describes a task position and dimensions.
+ *
+ * @property instanceId instance id of the task
+ * @property uid uid of the app associated with the task
+ * @property taskHeight height of the task in px
+ * @property taskWidth width of the task in px
+ * @property taskX x-coordinate of the top-left corner
+ * @property taskY y-coordinate of the top-left corner
+ *
+ */
data class TaskUpdate(
val instanceId: Int,
val uid: Int,
- val taskHeight: Int = Int.MIN_VALUE,
- val taskWidth: Int = Int.MIN_VALUE,
- val taskX: Int = Int.MIN_VALUE,
- val taskY: Int = Int.MIN_VALUE,
+ val taskHeight: Int,
+ val taskWidth: Int,
+ val taskX: Int,
+ val taskY: Int,
)
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 641952b..e710560 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -282,17 +282,23 @@
visibleFreeformTaskInfos.putAll(postTransitionVisibleFreeformTasks)
}
- // TODO(b/326231724) - Add logging around taskInfoChanges Updates
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
sessionId: Int,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>
) {
- // find new tasks that were added
postTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
- if (!preTransitionVisibleFreeformTasks.containsKey(taskId)) {
- desktopModeEventLogger.logTaskAdded(sessionId, buildTaskUpdateForTask(taskInfo))
+ val currentTaskUpdate = buildTaskUpdateForTask(taskInfo)
+ val previousTaskInfo = preTransitionVisibleFreeformTasks[taskId]
+ when {
+ // new tasks added
+ previousTaskInfo == null ->
+ desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate)
+ // old tasks that were resized or repositioned
+ // TODO(b/347935387): Log changes only once they are stable.
+ buildTaskUpdateForTask(previousTaskInfo) != currentTaskUpdate ->
+ desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate)
}
}
@@ -304,13 +310,17 @@
}
}
- // TODO(b/326231724: figure out how to get taskWidth and taskHeight from TaskInfo
private fun buildTaskUpdateForTask(taskInfo: TaskInfo): TaskUpdate {
- val taskUpdate = TaskUpdate(taskInfo.taskId, taskInfo.userId)
- // add task x, y if available
- taskInfo.positionInParent?.let { taskUpdate.copy(taskX = it.x, taskY = it.y) }
-
- return taskUpdate
+ val screenBounds = taskInfo.configuration.windowConfiguration.bounds
+ val positionInParent = taskInfo.positionInParent
+ return TaskUpdate(
+ instanceId = taskInfo.taskId,
+ uid = taskInfo.userId,
+ taskHeight = screenBounds.height(),
+ taskWidth = screenBounds.width(),
+ taskX = positionInParent.x,
+ taskY = positionInParent.y,
+ )
}
/** Get [EnterReason] for this session enter */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 7d01580..81891ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -46,6 +46,9 @@
val activeTasks: ArraySet<Int> = ArraySet(),
val visibleTasks: ArraySet<Int> = ArraySet(),
val minimizedTasks: ArraySet<Int> = ArraySet(),
+ // Tasks that are closing, but are still visible
+ // TODO(b/332682201): Remove when the repository state is updated via TransitionObserver
+ val closingTasks: ArraySet<Int> = ArraySet(),
// Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
)
@@ -169,6 +172,42 @@
return result
}
+ /**
+ * Mark a task with given [taskId] as closing on given [displayId]
+ *
+ * @return `true` if the task was not closing on given [displayId]
+ */
+ fun addClosingTask(displayId: Int, taskId: Int): Boolean {
+ val added = displayData.getOrCreate(displayId).closingTasks.add(taskId)
+ if (added) {
+ KtProtoLog.d(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTaskRepo: added closing task=%d displayId=%d",
+ taskId,
+ displayId
+ )
+ }
+ return added
+ }
+
+ /**
+ * Remove task with given [taskId] from closing tasks.
+ *
+ * @return `true` if the task was closing
+ */
+ fun removeClosingTask(taskId: Int): Boolean {
+ var removed = false
+ displayData.forEach { _, data ->
+ if (data.closingTasks.remove(taskId)) {
+ removed = true
+ }
+ }
+ if (removed) {
+ KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: remove closing task=%d", taskId)
+ }
+ return removed
+ }
+
/** Check if a task with the given [taskId] was marked as an active task */
fun isActiveTask(taskId: Int): Boolean {
return displayData.valueIterator().asSequence().any { data ->
@@ -176,6 +215,10 @@
}
}
+ /** Check if a task with the given [taskId] was marked as a closing task */
+ fun isClosingTask(taskId: Int): Boolean =
+ displayData.valueIterator().asSequence().any { data -> taskId in data.closingTasks }
+
/** Whether a task is visible. */
fun isVisibleTask(taskId: Int): Boolean {
return displayData.valueIterator().asSequence().any { data ->
@@ -190,12 +233,17 @@
}
}
- /** Check if a task with the given [taskId] is the only active task on its display */
- fun isOnlyActiveTask(taskId: Int): Boolean {
- return displayData.valueIterator().asSequence().any { data ->
- data.activeTasks.singleOrNull() == taskId
+ /**
+ * Check if a task with the given [taskId] is the only visible, non-closing, not-minimized task
+ * on its display
+ */
+ fun isOnlyVisibleNonClosingTask(taskId: Int): Boolean =
+ displayData.valueIterator().asSequence().any { data ->
+ data.visibleTasks
+ .subtract(data.closingTasks)
+ .subtract(data.minimizedTasks)
+ .singleOrNull() == taskId
}
- }
/** Get a set of the active tasks for given [displayId] */
fun getActiveTasks(displayId: Int): ArraySet<Int> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index c5111d6..5813f85 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -18,6 +18,7 @@
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
+import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.TaskInfo
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
@@ -40,7 +41,6 @@
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
-import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.RemoteTransition
import android.window.TransitionInfo
@@ -76,6 +76,7 @@
import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.shared.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
import com.android.wm.shell.shared.DesktopModeStatus.useDesktopOverrideDensity
+import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.annotations.ExternalThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -108,6 +109,7 @@
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
private val dragAndDropController: DragAndDropController,
private val transitions: Transitions,
+ private val keyguardManager: KeyguardManager,
private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
@@ -442,12 +444,21 @@
* active task.
*
* @param wct transaction to modify if the last active task is closed
+ * @param displayId display id of the window that's being closed
* @param taskId task id of the window that's being closed
*/
- fun onDesktopWindowClose(wct: WindowContainerTransaction, taskId: Int) {
- if (desktopModeTaskRepository.isOnlyActiveTask(taskId)) {
+ fun onDesktopWindowClose(wct: WindowContainerTransaction, displayId: Int, taskId: Int) {
+ if (desktopModeTaskRepository.isOnlyVisibleNonClosingTask(taskId)) {
removeWallpaperActivity(wct)
}
+ if (!desktopModeTaskRepository.addClosingTask(displayId, taskId)) {
+ // Could happen if the task hasn't been removed from closing list after it disappeared
+ KtProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: the task with taskId=%d is already closing!",
+ taskId
+ )
+ }
}
/** Move a task with given `taskId` to fullscreen */
@@ -870,8 +881,8 @@
reason = "recents animation is running"
false
}
- // Handle back navigation for the last window if wallpaper available
- shouldRemoveWallpaper(request) -> true
+ // Handle task closing for the last window if wallpaper is available
+ shouldHandleTaskClosing(request) -> true
// Only handle open or to front transitions
request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
reason = "transition type not handled (${request.type})"
@@ -909,7 +920,8 @@
val result =
triggerTask?.let { task ->
when {
- request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
+ // Check if the closing task needs to be handled
+ TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task)
// Check if the task has a top transparent activity
shouldLaunchAsModal(task) -> handleIncompatibleTaskLaunch(task)
// Check if the task has a top systemUI activity
@@ -951,13 +963,10 @@
private fun shouldLaunchAsModal(task: TaskInfo) =
Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
- private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean {
+ private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean {
return Flags.enableDesktopWindowingWallpaperActivity() &&
- request.type == TRANSIT_TO_BACK &&
- request.triggerTask?.let { task ->
- desktopModeTaskRepository.isOnlyActiveTask(task.taskId)
- }
- ?: false
+ TransitionUtil.isClosingType(request.type) &&
+ request.triggerTask != null
}
private fun handleFreeformTaskLaunch(
@@ -965,10 +974,16 @@
transition: IBinder
): WindowContainerTransaction? {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
+ if (keyguardManager.isKeyguardLocked) {
+ // Do NOT handle freeform task launch when locked.
+ // It will be launched in fullscreen windowing mode (Details: b/160925539)
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: skip keyguard is locked")
+ return null
+ }
if (!desktopModeTaskRepository.isDesktopModeShowing(task.displayId)) {
KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: switch freeform task to fullscreen oon transition" +
+ "DesktopTasksController: bring desktop tasks to front on transition" +
" taskId=%d",
task.taskId
)
@@ -1024,17 +1039,26 @@
return WindowContainerTransaction().also { wct -> addMoveToFullscreenChanges(wct, task) }
}
- /** Handle back navigation by removing wallpaper activity if it's the last active task */
- private fun handleBackNavigation(task: RunningTaskInfo): WindowContainerTransaction? {
- if (
- desktopModeTaskRepository.isOnlyActiveTask(task.taskId) &&
+ /** Handle task closing by removing wallpaper activity if it's the last active task */
+ private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
+ val wct = if (
+ desktopModeTaskRepository.isOnlyVisibleNonClosingTask(task.taskId) &&
desktopModeTaskRepository.wallpaperActivityToken != null
) {
// Remove wallpaper activity when the last active task is removed
- return WindowContainerTransaction().also { wct -> removeWallpaperActivity(wct) }
+ WindowContainerTransaction().also { wct -> removeWallpaperActivity(wct) }
} else {
- return null
+ null
}
+ if (!desktopModeTaskRepository.addClosingTask(task.displayId, task.taskId)) {
+ // Could happen if the task hasn't been removed from closing list after it disappeared
+ KtProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: the task with taskId=%d is already closing!",
+ task.taskId
+ )
+ }
+ return wct
}
private fun addMoveToDesktopChanges(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
index 1385f42..7ad68aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -5,3 +5,4 @@
nmusgrave@google.com
pbdr@google.com
tkachenkoi@google.com
+vaniadesmonda@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index 88d0554..5335c0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -27,6 +27,8 @@
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.core.animation.addListener
+import com.android.internal.jank.Cuj
+import com.android.wm.shell.common.InteractionJankMonitorUtils
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
@@ -103,6 +105,8 @@
onTaskResizeAnimationListener.onAnimationEnd(taskId)
finishCallback.onTransitionFinished(null)
boundsAnimator = null
+ InteractionJankMonitorUtils.endTracing(
+ Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
}
)
addUpdateListener { anim ->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index 438aa76..b1cbe8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -73,7 +73,7 @@
following system properties for example:
```shell
# Enabling
-adb shell setprop persist.wm.debug.sc.tx.log_match_call setAlpha # matches the name of the SurfaceControlTransaction method
+adb shell setprop persist.wm.debug.sc.tx.log_match_call setAlpha,setPosition # matches the name of the SurfaceControlTransaction methods
adb shell setprop persist.wm.debug.sc.tx.log_match_name com.android.systemui # matches the name of the surface
adb reboot
adb logcat -s "SurfaceControlRegistry"
@@ -87,6 +87,16 @@
It is not necessary to set both `log_match_call` and `log_match_name`, but note logs can be quite
noisy if unfiltered.
+It can sometimes be useful to trace specific logs and when they are applied (sometimes we build
+transactions that can be applied later). You can do this by adding the "merge" and "apply" calls to
+the set of requested calls:
+```shell
+# Enabling
+adb shell setprop persist.wm.debug.sc.tx.log_match_call setAlpha,merge,apply # apply will dump logs of each setAlpha or merge call on that tx
+adb reboot
+adb logcat -s "SurfaceControlRegistry"
+```
+
## Tracing activity starts in the app process
It's sometimes useful to know when to see a stack trace of when an activity starts in the app code
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 7d2aa27..b48aee5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -150,6 +150,10 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
}
+ } else if (repository.isClosingTask(taskInfo.taskId)
+ && repository.removeClosingTask(taskInfo.taskId)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Removing closing freeform task: #%d", taskInfo.taskId);
}
repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId,
taskInfo.isVisible);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 202f60d..3d1994c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -137,7 +137,7 @@
mTmpDestinationRect.inset(insets);
// Scale to the bounds no smaller than the destination and offset such that the top/left
// of the scaled inset source rect aligns with the top/left of the destination bounds
- final float scale, left, top;
+ final float scale;
if (isInPipDirection
&& sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) {
// scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only.
@@ -148,14 +148,17 @@
? (float) destinationBounds.width() / sourceBounds.width()
: (float) destinationBounds.height() / sourceBounds.height();
scale = (1 - fraction) * startScale + fraction * endScale;
- left = destinationBounds.left - insets.left * scale;
- top = destinationBounds.top - insets.top * scale;
} else {
scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
(float) destinationBounds.height() / sourceBounds.height());
- // Work around the rounding error by fix the position at very beginning.
- left = scale == 1 ? 0 : destinationBounds.left - insets.left * scale;
- top = scale == 1 ? 0 : destinationBounds.top - insets.top * scale;
+ }
+ float left = destinationBounds.left - insets.left * scale;
+ float top = destinationBounds.top - insets.top * scale;
+ if (scale == 1) {
+ // Work around the 1 pixel off error by rounding the position down at very beginning.
+ // We noticed such error from flicker tests, not visually.
+ left = sourceBounds.left;
+ top = sourceBounds.top;
}
mTmpTransform.setScale(scale, scale);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e4420d7..e2e1ecd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -63,7 +63,6 @@
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.util.Rational;
import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
@@ -128,8 +127,6 @@
SystemProperties.getInt(
"persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400);
- private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.005f;
-
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -608,6 +605,7 @@
public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
if (!mPipTransitionState.isInPip()
|| mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
+ || mPipTransitionState.getInSwipePipToHomeTransition()
|| mToken == null) {
ProtoLog.wtf(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Not allowed to exitPip in current state"
@@ -821,37 +819,6 @@
mPictureInPictureParams.getTitle());
mPipParamsChangedForwarder.notifySubtitleChanged(
mPictureInPictureParams.getSubtitle());
-
- if (mPictureInPictureParams.hasSourceBoundsHint()
- && mPictureInPictureParams.hasSetAspectRatio()) {
- Rational sourceRectHintAspectRatio = new Rational(
- mPictureInPictureParams.getSourceRectHint().width(),
- mPictureInPictureParams.getSourceRectHint().height());
- if (sourceRectHintAspectRatio.compareTo(
- mPictureInPictureParams.getAspectRatio()) != 0) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "Aspect ratio of source rect hint (%d/%d) does not match the provided "
- + "aspect ratio value (%d/%d). Consider matching them for "
- + "improved animation. Future releases might override the "
- + "value to match.",
- mPictureInPictureParams.getSourceRectHint().width(),
- mPictureInPictureParams.getSourceRectHint().height(),
- mPictureInPictureParams.getAspectRatio().getNumerator(),
- mPictureInPictureParams.getAspectRatio().getDenominator());
- }
- if (Math.abs(sourceRectHintAspectRatio.floatValue()
- - mPictureInPictureParams.getAspectRatioFloat())
- > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "Aspect ratio of source rect hint (%f) does not match the provided "
- + "aspect ratio value (%f) and is above threshold of %f. "
- + "Consider matching them for improved animation. Future "
- + "releases might override the value to match.",
- sourceRectHintAspectRatio.floatValue(),
- mPictureInPictureParams.getAspectRatioFloat(),
- PIP_ASPECT_RATIO_MISMATCH_THRESHOLD);
- }
- }
}
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 03c8cf8..ad298dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -65,9 +65,11 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -394,6 +396,7 @@
}
ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
+ Set<Integer> minimizedFreeformTasks = new HashSet<>();
int mostRecentFreeformTaskIndex = Integer.MAX_VALUE;
@@ -409,15 +412,14 @@
if (DesktopModeStatus.canEnterDesktopMode(mContext)
&& mDesktopModeTaskRepository.isPresent()
&& mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) {
- if (mDesktopModeTaskRepository.get().isMinimizedTask(taskInfo.taskId)) {
- // Minimized freeform tasks should not be shown at all.
- continue;
- }
// Freeform tasks will be added as a separate entry
if (mostRecentFreeformTaskIndex == Integer.MAX_VALUE) {
mostRecentFreeformTaskIndex = recentTasks.size();
}
freeformTasks.add(taskInfo);
+ if (mDesktopModeTaskRepository.get().isMinimizedTask(taskInfo.taskId)) {
+ minimizedFreeformTasks.add(taskInfo.taskId);
+ }
continue;
}
@@ -435,8 +437,10 @@
// Add a special entry for freeform tasks
if (!freeformTasks.isEmpty()) {
- recentTasks.add(mostRecentFreeformTaskIndex, GroupedRecentTaskInfo.forFreeformTasks(
- freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
+ recentTasks.add(mostRecentFreeformTaskIndex,
+ GroupedRecentTaskInfo.forFreeformTasks(
+ freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0]),
+ minimizedFreeformTasks));
}
return recentTasks;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 3a266d9..c67cf1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -74,6 +74,7 @@
import java.util.ArrayList;
import java.util.function.Consumer;
+import java.util.function.Supplier;
/**
* Handles the Recents (overview) animation. Only one of these can run at a time. A recents
@@ -84,6 +85,7 @@
private final Transitions mTransitions;
private final ShellExecutor mExecutor;
+ private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
@Nullable
private final RecentTasksController mRecentTasksController;
private IApplicationThread mAnimApp = null;
@@ -101,11 +103,13 @@
public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
@Nullable RecentTasksController recentTasksController,
- HomeTransitionObserver homeTransitionObserver) {
+ HomeTransitionObserver homeTransitionObserver,
+ Supplier<SurfaceControl.Transaction> transactionSupplier) {
mTransitions = transitions;
mExecutor = transitions.getMainExecutor();
mRecentTasksController = recentTasksController;
mHomeTransitionObserver = homeTransitionObserver;
+ mTransactionSupplier = transactionSupplier;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
if (recentTasksController == null) return;
shellInit.addInitCallback(() -> {
@@ -1056,7 +1060,7 @@
final Transitions.TransitionFinishCallback finishCB = mFinishCB;
mFinishCB = null;
- final SurfaceControl.Transaction t = mFinishTransaction;
+ SurfaceControl.Transaction t = mFinishTransaction;
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (mKeyguardLocked && mRecentsTask != null) {
@@ -1106,6 +1110,16 @@
}
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " normal finish");
+ if (toHome && !mOpeningTasks.isEmpty()) {
+ // Attempting to start a task after swipe to home, don't show it,
+ // move recents to top
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " attempting to start a task after swipe to home");
+ t = mTransactionSupplier.get();
+ wct.reorder(mRecentsTask, true /*onTop*/);
+ mClosingTasks.addAll(mOpeningTasks);
+ mOpeningTasks.clear();
+ }
// The general case: committing to recents, going home, or switching tasks.
for (int i = 0; i < mOpeningTasks.size(); ++i) {
t.show(mOpeningTasks.get(i).mTaskSurface);
@@ -1174,6 +1188,10 @@
mPipTransaction = null;
}
}
+ if (t != mFinishTransaction) {
+ // apply after merges because these changes are accounting for finishWCT changes.
+ mTransitions.setAfterMergeFinishTransaction(mTransition, t);
+ }
cleanUp();
finishCB.onTransitionFinished(wct.isEmpty() ? null : wct);
if (runnerFinishCb != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
index 7c5f10a..8ee72b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -76,21 +76,40 @@
continue
}
+ // Filter out changes that we care about
if (change.mode == WindowManager.TRANSIT_OPEN) {
change.taskInfo?.let { taskInfoList.add(it) }
transitionTypeList.add(change.mode)
}
}
- transitionToTransitionChanges.put(
- transition,
- TransitionChanges(taskInfoList, transitionTypeList)
- )
+ // Only add the transition to map if it has a change we care about
+ if (taskInfoList.isNotEmpty()) {
+ transitionToTransitionChanges.put(
+ transition,
+ TransitionChanges(taskInfoList, transitionTypeList)
+ )
+ }
}
}
override fun onTransitionStarting(transition: IBinder) {}
- override fun onTransitionMerged(merged: IBinder, playing: IBinder) {}
+ override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+ val mergedTransitionChanges =
+ transitionToTransitionChanges.get(merged)
+ ?:
+ // We are adding changes of the merged transition to changes of the playing
+ // transition so if there is no changes nothing to do.
+ return
+
+ transitionToTransitionChanges.remove(merged)
+ val playingTransitionChanges = transitionToTransitionChanges.get(playing)
+ if (playingTransitionChanges != null) {
+ playingTransitionChanges.merge(mergedTransitionChanges)
+ } else {
+ transitionToTransitionChanges.put(playing, mergedTransitionChanges)
+ }
+ }
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
val taskInfoList =
@@ -138,6 +157,11 @@
private data class TransitionChanges(
val taskInfoList: MutableList<RunningTaskInfo> = ArrayList(),
- val transitionTypeList: MutableList<Int> = ArrayList()
- )
+ val transitionTypeList: MutableList<Int> = ArrayList(),
+ ) {
+ fun merge(transitionChanges: TransitionChanges) {
+ taskInfoList.addAll(transitionChanges.taskInfoList)
+ transitionTypeList.addAll(transitionChanges.transitionTypeList)
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b6a18e5..45eff4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2649,7 +2649,7 @@
@Nullable TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
- if (isSplitActive()) {
+ if (isSplitScreenVisible()) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
request.getDebugId());
// Check if the display is rotating.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 66b3553..8fc54ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -21,8 +21,6 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
-
import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,7 +28,6 @@
import android.app.ActivityManager.TaskDescription;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -139,16 +136,10 @@
}
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
- if (windowSessionRelayoutInfo()) {
- final WindowRelayoutResult outRelayoutResult = new WindowRelayoutResult(tmpFrames,
- tmpMergedConfiguration, surfaceControl, tmpInsetsState, tmpControls);
- session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0,
- outRelayoutResult);
- } else {
- session.relayoutLegacy(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0,
- tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
- tmpControls, new Bundle());
- }
+ final WindowRelayoutResult outRelayoutResult = new WindowRelayoutResult(tmpFrames,
+ tmpMergedConfiguration, surfaceControl, tmpInsetsState, tmpControls);
+ session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0,
+ outRelayoutResult);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
index b03daaa..35427b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
@@ -94,6 +94,11 @@
return rotatedBounds;
}
+ /** Returns true if the change is put on a surface in previous rotation. */
+ public boolean isRotated(@NonNull TransitionInfo.Change change) {
+ return mLastRotationDelta != 0 && mRotatorMap.containsKey(change.getParent());
+ }
+
/**
* Removes the counter rotation surface in the finish transaction. No need to reparent the
* children as the finish transaction should have already taken care of that.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 018c904..9db153f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -53,6 +53,7 @@
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CHANGE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
@@ -517,7 +518,8 @@
animRelOffset.y = Math.max(animRelOffset.y, change.getEndRelOffset().y);
}
- if (change.getActivityComponent() != null && !isActivityLevel) {
+ if (change.getActivityComponent() != null && !isActivityLevel
+ && !mRotator.isRotated(change)) {
// At this point, this is an independent activity change in a non-activity
// transition. This means that an activity transition got erroneously combined
// with another ongoing transition. This then means that the animation root may
@@ -943,12 +945,15 @@
}
private static int getWallpaperTransitType(TransitionInfo info) {
+ boolean hasWallpaper = false;
boolean hasOpenWallpaper = false;
boolean hasCloseWallpaper = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) {
+ if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0
+ || (change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ hasWallpaper = true;
if (TransitionUtil.isOpeningType(change.getMode())) {
hasOpenWallpaper = true;
} else if (TransitionUtil.isClosingType(change.getMode())) {
@@ -964,6 +969,8 @@
return WALLPAPER_TRANSITION_OPEN;
} else if (hasCloseWallpaper) {
return WALLPAPER_TRANSITION_CLOSE;
+ } else if (hasWallpaper) {
+ return WALLPAPER_TRANSITION_CHANGE;
} else {
return WALLPAPER_TRANSITION_NONE;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index f257e20..f6e38da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -28,12 +28,15 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
+import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -238,6 +241,13 @@
/** Ordered list of transitions which have been merged into this one. */
private ArrayList<ActiveTransition> mMerged;
+ /**
+ * @deprecated DO NOT USE THIS unless absolutely necessary. It will be removed once
+ * everything migrates off finishWCT.
+ */
+ @java.lang.Deprecated
+ SurfaceControl.Transaction mAfterMergeFinishT;
+
ActiveTransition(IBinder token) {
mToken = token;
}
@@ -512,12 +522,17 @@
boolean isOpening = isOpeningType(info.getType());
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.hasFlags(TransitionInfo.FLAGS_IS_NON_APP_WINDOW)) {
+ if (change.hasFlags(FLAGS_IS_NON_APP_WINDOW & ~FLAG_IS_WALLPAPER)) {
// Currently system windows are controlled by WindowState, so don't change their
// surfaces. Otherwise their surfaces could be hidden or cropped unexpectedly.
- // This includes Wallpaper (always z-ordered at bottom) and IME (associated with
- // app), because there may not be a transition associated with their visibility
- // changes, and currently they don't need transition animation.
+ // This includes IME (associated with app), because there may not be a transition
+ // associated with their visibility changes, and currently they don't need a
+ // transition animation.
+ continue;
+ }
+ if (change.hasFlags(FLAG_IS_WALLPAPER) && !ensureWallpaperInTransitions()) {
+ // Wallpaper is always z-ordered at bottom, and historically is not animated by
+ // transition handlers.
continue;
}
final SurfaceControl leash = change.getLeash();
@@ -1018,6 +1033,20 @@
return null;
}
+ /** @deprecated */
+ @java.lang.Deprecated
+ public void setAfterMergeFinishTransaction(IBinder transition,
+ SurfaceControl.Transaction afterMergeFinishT) {
+ final ActiveTransition at = mKnownTransitions.get(transition);
+ if (at == null) return;
+ if (at.mAfterMergeFinishT != null) {
+ Log.e(TAG, "Setting after-merge-t >1 time on transition: " + at.mInfo.getDebugId());
+ at.mAfterMergeFinishT.merge(afterMergeFinishT);
+ return;
+ }
+ at.mAfterMergeFinishT = afterMergeFinishT;
+ }
+
/** Aborts a transition. This will still queue it up to maintain order. */
private void onAbort(ActiveTransition transition) {
final Track track = mTracks.get(transition.getTrack());
@@ -1078,6 +1107,7 @@
}
// Merge all associated transactions together
SurfaceControl.Transaction fullFinish = active.mFinishT;
+ SurfaceControl.Transaction afterMergeFinish = active.mAfterMergeFinishT;
if (active.mMerged != null) {
for (int iM = 0; iM < active.mMerged.size(); ++iM) {
final ActiveTransition toMerge = active.mMerged.get(iM);
@@ -1097,6 +1127,21 @@
fullFinish.merge(toMerge.mFinishT);
}
}
+ if (toMerge.mAfterMergeFinishT != null) {
+ if (afterMergeFinish == null) {
+ afterMergeFinish = toMerge.mAfterMergeFinishT;
+ } else {
+ afterMergeFinish.merge(toMerge.mAfterMergeFinishT);
+ }
+ toMerge.mAfterMergeFinishT = null;
+ }
+ }
+ }
+ if (afterMergeFinish != null) {
+ if (fullFinish == null) {
+ fullFinish = afterMergeFinish;
+ } else {
+ fullFinish.merge(afterMergeFinish);
}
}
if (fullFinish != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index c045ceb..a2d2b9a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -27,6 +27,7 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
/**
* Simple container for recent tasks. May contain either a single or pair of tasks.
@@ -50,6 +51,9 @@
private final SplitBounds mSplitBounds;
@GroupType
private final int mType;
+ // TODO(b/348332802): move isMinimized inside each Task object instead once we have a
+ // replacement for RecentTaskInfo
+ private final int[] mMinimizedTaskIds;
/**
* Create new for a single task
@@ -57,7 +61,7 @@
public static GroupedRecentTaskInfo forSingleTask(
@NonNull ActivityManager.RecentTaskInfo task) {
return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
- TYPE_SINGLE);
+ TYPE_SINGLE, null /* minimizedFreeformTasks */);
}
/**
@@ -66,28 +70,51 @@
public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
@NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
- splitBounds, TYPE_SPLIT);
+ splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */);
}
/**
* Create new for a group of freeform tasks
*/
public static GroupedRecentTaskInfo forFreeformTasks(
- @NonNull ActivityManager.RecentTaskInfo... tasks) {
- return new GroupedRecentTaskInfo(tasks, null, TYPE_FREEFORM);
+ @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @NonNull Set<Integer> minimizedFreeformTasks) {
+ return new GroupedRecentTaskInfo(
+ tasks,
+ null /* splitBounds */,
+ TYPE_FREEFORM,
+ minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
}
- private GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo[] tasks,
- @Nullable SplitBounds splitBounds, @GroupType int type) {
+ private GroupedRecentTaskInfo(
+ @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @Nullable SplitBounds splitBounds,
+ @GroupType int type,
+ @Nullable int[] minimizedFreeformTaskIds) {
mTasks = tasks;
mSplitBounds = splitBounds;
mType = type;
+ mMinimizedTaskIds = minimizedFreeformTaskIds;
+ ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
+ }
+
+ private static void ensureAllMinimizedIdsPresent(
+ @NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @Nullable int[] minimizedFreeformTaskIds) {
+ if (minimizedFreeformTaskIds == null) {
+ return;
+ }
+ if (!Arrays.stream(minimizedFreeformTaskIds).allMatch(
+ taskId -> Arrays.stream(tasks).anyMatch(task -> task.taskId == taskId))) {
+ throw new IllegalArgumentException("Minimized task IDs contain non-existent Task ID.");
+ }
}
GroupedRecentTaskInfo(Parcel parcel) {
mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
mType = parcel.readInt();
+ mMinimizedTaskIds = parcel.createIntArray();
}
/**
@@ -135,6 +162,10 @@
return mType;
}
+ public int[] getMinimizedTaskIds() {
+ return mMinimizedTaskIds;
+ }
+
@Override
public String toString() {
StringBuilder taskString = new StringBuilder();
@@ -161,6 +192,8 @@
taskString.append("TYPE_FREEFORM");
break;
}
+ taskString.append(", Minimized Task IDs: ");
+ taskString.append(Arrays.toString(mMinimizedTaskIds));
return taskString.toString();
}
@@ -181,6 +214,7 @@
parcel.writeTypedArray(mTasks, flags);
parcel.writeTypedObject(mSplitBounds, flags);
parcel.writeInt(mType);
+ parcel.writeIntArray(mMinimizedTaskIds);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 21b6db2..e1009a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -73,6 +73,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.Cuj;
import com.android.internal.protolog.common.ProtoLog;
import com.android.window.flags.Flags;
import com.android.wm.shell.R;
@@ -81,6 +82,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.InteractionJankMonitorUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
@@ -435,7 +437,7 @@
SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
WindowContainerTransaction wct = new WindowContainerTransaction();
- mDesktopTasksController.onDesktopWindowClose(wct, mTaskId);
+ mDesktopTasksController.onDesktopWindowClose(wct, mDisplayId, mTaskId);
mTaskOperations.closeTask(mTaskToken, wct);
}
} else if (id == R.id.back_button) {
@@ -470,11 +472,17 @@
} else if (id == R.id.collapse_menu_button) {
decoration.closeHandleMenu();
} else if (id == R.id.maximize_window) {
+ InteractionJankMonitorUtils.beginTracing(
+ Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, /* view= */ v,
+ /* tag= */ "caption_bar_button");
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
mDesktopTasksController.toggleDesktopTaskSize(taskInfo);
} else if (id == R.id.maximize_menu_maximize_button) {
+ InteractionJankMonitorUtils.beginTracing(
+ Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, /* view= */ v,
+ /* tag= */ "maximize_menu_option");
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
mDesktopTasksController.toggleDesktopTaskSize(taskInfo);
decoration.closeHandleMenu();
@@ -712,6 +720,9 @@
return false;
}
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ InteractionJankMonitorUtils.beginTracing(
+ Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, mContext,
+ /* surface= */ decoration.mTaskSurface, /* tag= */ "double_tap");
mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index fe1c9c3..d48ce53 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -28,6 +28,8 @@
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
+import androidx.annotation.NonNull;
+
import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
@@ -106,13 +108,15 @@
repositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
? candidateBottom : oldBottom;
}
- // If width or height are negative or less than the minimum width or height, revert the
+ // If width or height are negative or exceeding the width or height constraints, revert the
// respective bounds to use previous bound dimensions.
- if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) {
+ if (isExceedingWidthConstraint(repositionTaskBounds, stableBounds, displayController,
+ windowDecoration)) {
repositionTaskBounds.right = oldRight;
repositionTaskBounds.left = oldLeft;
}
- if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) {
+ if (isExceedingHeightConstraint(repositionTaskBounds, stableBounds, displayController,
+ windowDecoration)) {
repositionTaskBounds.top = oldTop;
repositionTaskBounds.bottom = oldBottom;
}
@@ -174,6 +178,30 @@
return result;
}
+ private static boolean isExceedingWidthConstraint(@NonNull Rect repositionTaskBounds,
+ Rect maxResizeBounds, DisplayController displayController,
+ WindowDecoration windowDecoration) {
+ // Check if width is less than the minimum width constraint.
+ if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) {
+ return true;
+ }
+ // Check if width is more than the maximum resize bounds on desktop windowing mode.
+ return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)
+ && repositionTaskBounds.width() > maxResizeBounds.width();
+ }
+
+ private static boolean isExceedingHeightConstraint(@NonNull Rect repositionTaskBounds,
+ Rect maxResizeBounds, DisplayController displayController,
+ WindowDecoration windowDecoration) {
+ // Check if height is less than the minimum height constraint.
+ if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) {
+ return true;
+ }
+ // Check if height is more than the maximum resize bounds on desktop windowing mode.
+ return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)
+ && repositionTaskBounds.height() > maxResizeBounds.height();
+ }
+
private static float getMinWidth(DisplayController displayController,
WindowDecoration windowDecoration) {
return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinWidth(displayController,
@@ -210,7 +238,7 @@
private static float getDefaultMinSize(DisplayController displayController,
WindowDecoration windowDecoration) {
- float density = displayController.getDisplayLayout(windowDecoration.mTaskInfo.displayId)
+ float density = displayController.getDisplayLayout(windowDecoration.mTaskInfo.displayId)
.densityDpi() * DisplayMetrics.DENSITY_DEFAULT_SCALE;
return windowDecoration.mTaskInfo.defaultMinSize * density;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 5fce5d2..956d04c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -18,6 +18,8 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_RESIZE_WINDOW;
+
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -33,6 +35,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.InteractionJankMonitorUtils;
import com.android.wm.shell.transition.Transitions;
import java.util.function.Supplier;
@@ -89,6 +92,10 @@
mDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
mRepositionStartPoint.set(x, y);
if (isResizing()) {
+ // Capture CUJ for re-sizing window in DW mode.
+ InteractionJankMonitorUtils.beginTracing(CUJ_DESKTOP_MODE_RESIZE_WINDOW,
+ mDesktopWindowDecoration.mContext, mDesktopWindowDecoration.mTaskSurface,
+ /* tag= */ null);
if (!mDesktopWindowDecoration.mTaskInfo.isFocused) {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true);
@@ -146,6 +153,7 @@
// won't be called.
resetVeilIfVisible();
}
+ InteractionJankMonitorUtils.endTracing(CUJ_DESKTOP_MODE_RESIZE_WINDOW);
} else {
final WindowContainerTransaction wct = new WindowContainerTransaction();
DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
index 0fe7a16b..3f2603a 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
@@ -37,17 +37,51 @@
"src/**/B*.kt",
"src/**/C*.kt",
"src/**/D*.kt",
- "src/**/E*.kt",
],
}
filegroup {
name: "WMShellFlickerTestsSplitScreenGroup2-src",
srcs: [
+ "src/**/E*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreenGroup3-src",
+ srcs: [
+ "src/**/S*.kt",
+ ],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreenGroupOther-src",
+ srcs: [
"src/**/*.kt",
],
}
+java_library {
+ name: "WMShellFlickerTestsSplitScreenBase",
+ srcs: [
+ ":WMShellFlickerTestsSplitScreenBase-src",
+ ],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "wm-shell-flicker-utils",
+ "androidx.test.ext.junit",
+ "flickertestapplib",
+ "flickerlib",
+ "flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
+ "platform-test-annotations",
+ "wm-flicker-common-app-helpers",
+ "wm-flicker-common-assertions",
+ "launcher-helper-lib",
+ "launcher-aosp-tapl",
+ ],
+}
+
android_test {
name: "WMShellFlickerTestsSplitScreenGroup1",
defaults: ["WMShellFlickerTestsDefault"],
@@ -56,10 +90,12 @@
instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
test_config_template: "AndroidTestTemplate.xml",
srcs: [
- ":WMShellFlickerTestsSplitScreenBase-src",
":WMShellFlickerTestsSplitScreenGroup1-src",
],
- static_libs: ["WMShellFlickerTestsBase"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellFlickerTestsSplitScreenBase",
+ ],
data: ["trace_config/*"],
}
@@ -71,12 +107,50 @@
instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
test_config_template: "AndroidTestTemplate.xml",
srcs: [
- ":WMShellFlickerTestsSplitScreenBase-src",
- ":WMShellFlickerTestsSplitScreenGroup2-src",
+ ":WMShellFlickerTestsSplitScreenGroup1-src",
+ ],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellFlickerTestsSplitScreenBase",
+ ],
+ data: ["trace_config/*"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreenGroup3",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.splitscreen",
+ instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: [
+ ":WMShellFlickerTestsSplitScreenGroup1-src",
+ ],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellFlickerTestsSplitScreenBase",
+ ],
+ data: ["trace_config/*"],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreenGroupOther",
+ defaults: ["WMShellFlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.wm.shell.flicker.splitscreen",
+ instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: [
+ ":WMShellFlickerTestsSplitScreenGroupOther-src",
],
exclude_srcs: [
":WMShellFlickerTestsSplitScreenGroup1-src",
+ ":WMShellFlickerTestsSplitScreenGroup2-src",
+ ":WMShellFlickerTestsSplitScreenGroup3-src",
],
- static_libs: ["WMShellFlickerTestsBase"],
+ static_libs: [
+ "WMShellFlickerTestsBase",
+ "WMShellFlickerTestsSplitScreenBase",
+ ],
data: ["trace_config/*"],
}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 13f95cc..92be4f9 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -46,6 +46,7 @@
"androidx.dynamicanimation_dynamicanimation",
"dagger2",
"frameworks-base-testutils",
+ "kotlin-test",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"mockito-kotlin2",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index fb03f20..665bed0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -19,6 +19,8 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
@@ -36,11 +38,12 @@
import android.window.TransitionInfo.Change
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
@@ -53,22 +56,23 @@
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
-import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.same
+import org.mockito.kotlin.spy
import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
/**
* Test class for {@link DesktopModeLoggerTransitionObserver}
@@ -77,20 +81,17 @@
*/
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class DesktopModeLoggerTransitionObserverTest {
+class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
@JvmField
@Rule
val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this)
- .mockStatic(DesktopModeEventLogger::class.java)
- .mockStatic(DesktopModeStatus::class.java)
- .build()!!
+ ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!!
- @Mock lateinit var testExecutor: ShellExecutor
- @Mock private lateinit var mockShellInit: ShellInit
- @Mock private lateinit var transitions: Transitions
- @Mock private lateinit var context: Context
+ private val testExecutor = mock<ShellExecutor>()
+ private val mockShellInit = mock<ShellInit>()
+ private val transitions = mock<Transitions>()
+ private val context = mock<Context>()
private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
private lateinit var shellInit: ShellInit
@@ -98,9 +99,9 @@
@Before
fun setup() {
- doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
- shellInit = Mockito.spy(ShellInit(testExecutor))
- desktopModeEventLogger = mock(DesktopModeEventLogger::class.java)
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
+ shellInit = spy(ShellInit(testExecutor))
+ desktopModeEventLogger = mock<DesktopModeEventLogger>()
transitionObserver =
DesktopModeLoggerTransitionObserver(
@@ -121,7 +122,7 @@
@Test
fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
- val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
@@ -132,22 +133,17 @@
@Test
fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
- val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT, DEFAULT_TASK_UPDATE)
}
@Test
fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
// task change is finalised when drag ends
val transitionInfo =
TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
@@ -155,82 +151,57 @@
.build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG, DEFAULT_TASK_UPDATE)
}
@Test
fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
.addChange(change)
.build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON, DEFAULT_TASK_UPDATE)
}
@Test
fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
.addChange(change)
.build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
.addChange(change)
.build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER, DEFAULT_TASK_UPDATE)
}
@Test
fun transitToFront_logTaskAddedAndEnterReasonOverview() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
@@ -238,35 +209,29 @@
// previous exit to overview transition
val previousSessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
transitionObserver.setLoggerSessionId(previousSessionId)
- val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
val previousTransitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(previousChange)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
.build()
callOnTransitionReady(previousTransitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+ verifyTaskRemovedAndExitLogging(
+ previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
// Enter desktop mode from cancelled recents has no transition. Enter is detected on the
// next transition involving freeform windows
// TRANSIT_TO_FRONT
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
@@ -274,35 +239,29 @@
// previous exit to overview transition
val previousSessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
transitionObserver.setLoggerSessionId(previousSessionId)
- val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
val previousTransitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(previousChange)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
.build()
callOnTransitionReady(previousTransitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+ verifyTaskRemovedAndExitLogging(
+ previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
// Enter desktop mode from cancelled recents has no transition. Enter is detected on the
// next transition involving freeform windows
// TRANSIT_CHANGE
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
@@ -310,35 +269,29 @@
// previous exit to overview transition
val previousSessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
transitionObserver.setLoggerSessionId(previousSessionId)
- val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
val previousTransitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(previousChange)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
.build()
callOnTransitionReady(previousTransitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+ verifyTaskRemovedAndExitLogging(
+ previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
// Enter desktop mode from cancelled recents has no transition. Enter is detected on the
// next transition involving freeform windows
// TRANSIT_OPEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
@@ -349,286 +302,389 @@
// previous exit to overview transition
val previousSessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
transitionObserver.setLoggerSessionId(previousSessionId)
- val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
val previousTransitionInfo =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(previousChange)
- .build()
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+ .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
+ .build()
callOnTransitionReady(previousTransitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+ verifyTaskRemovedAndExitLogging(
+ previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
// TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
- .addChange(change)
- .build()
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+ .addChange(change)
+ .build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER, DEFAULT_TASK_UPDATE)
}
@Test
fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
+ verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON, DEFAULT_TASK_UPDATE)
}
@Test
- fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() {
+ fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() {
val sessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
}
@Test
fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
val sessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
// window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
val sessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
// window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
.addChange(change)
.build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ verifyTaskRemovedAndExitLogging(
+ sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
val sessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
// window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ verifyTaskRemovedAndExitLogging(
+ sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
val sessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
// window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
val sessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
// recents transition
- val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
val transitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ verifyTaskRemovedAndExitLogging(
+ sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
val sessionId = 1
// add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
// task closing
- val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
}
@Test
fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
val sessionId = 1
// add a freeform task to an existing session
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo)
transitionObserver.setLoggerSessionId(sessionId)
// recents transition sent freeform window to back
- val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_TO_BACK, taskInfo)
val transitionInfo1 =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
callOnTransitionReady(transitionInfo1)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
+
+ verifyTaskRemovedAndExitLogging(
+ sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
callOnTransitionReady(transitionInfo2)
- verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any())
- verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any())
+ verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
}
@Test
fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
val sessionId = 1
// add an existing freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.setLoggerSessionId(sessionId)
// new freeform task added
- val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logTaskAdded(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2)))
verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
}
@Test
+ fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
+ val sessionId = 1
+ // add an existing freeform task
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo)
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // task position changed
+ val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100)))
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
+ val sessionId = 1
+ // add an existing freeform task
+ val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo)
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // task resized
+ val newTaskInfo =
+ createTaskInfo(
+ WINDOWING_MODE_FREEFORM,
+ taskWidth = DEFAULT_TASK_WIDTH + 100,
+ taskHeight = DEFAULT_TASK_HEIGHT - 100)
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo))
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(sessionId),
+ eq(
+ DEFAULT_TASK_UPDATE.copy(
+ taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100)))
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
+ val sessionId = 1
+ // add 2 existing freeform task
+ val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
+ val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo1)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo2)
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // task 1 position update
+ val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
+ val transitionInfo1 =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo1))
+ .build()
+ callOnTransitionReady(transitionInfo1)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100)))
+ verifyZeroInteractions(desktopModeEventLogger)
+
+ // task 2 resize
+ val newTaskInfo2 =
+ createTaskInfo(
+ WINDOWING_MODE_FREEFORM,
+ id = 2,
+ taskWidth = DEFAULT_TASK_WIDTH + 100,
+ taskHeight = DEFAULT_TASK_HEIGHT - 100)
+ val transitionInfo2 =
+ TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .addChange(createChange(TRANSIT_CHANGE, newTaskInfo2))
+ .build()
+
+ callOnTransitionReady(transitionInfo2)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskInfoChanged(
+ eq(sessionId),
+ eq(
+ DEFAULT_TASK_UPDATE.copy(
+ instanceId = 2,
+ taskWidth = DEFAULT_TASK_WIDTH + 100,
+ taskHeight = DEFAULT_TASK_HEIGHT - 100)))
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
val sessionId = 1
// add two existing freeform tasks
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
transitionObserver.setLoggerSessionId(sessionId)
- // new freeform task added
- val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+ // new freeform task closed
+ val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2)))
verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
}
/** Simulate calling the onTransitionReady() method */
private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
- val transition = mock(IBinder::class.java)
- val startT = mock(SurfaceControl.Transaction::class.java)
- val finishT = mock(SurfaceControl.Transaction::class.java)
+ val transition = mock<IBinder>()
+ val startT = mock<SurfaceControl.Transaction>()
+ val finishT = mock<SurfaceControl.Transaction>()
transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
}
- companion object {
- fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo {
- val taskInfo = ActivityManager.RunningTaskInfo()
- taskInfo.taskId = taskId
- taskInfo.configuration.windowConfiguration.windowingMode = windowMode
+ private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
+ val sessionId = transitionObserver.getLoggerSessionId()
+ assertNotNull(sessionId)
+ verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate))
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
- return taskInfo
- }
+ private fun verifyTaskRemovedAndExitLogging(
+ sessionId: Int,
+ exitReason: ExitReason,
+ taskUpdate: TaskUpdate
+ ) {
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate))
+ verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertNull(transitionObserver.getLoggerSessionId())
+ }
+
+ private companion object {
+ const val DEFAULT_TASK_ID = 1
+ const val DEFAULT_TASK_UID = 2
+ const val DEFAULT_TASK_HEIGHT = 100
+ const val DEFAULT_TASK_WIDTH = 200
+ const val DEFAULT_TASK_X = 30
+ const val DEFAULT_TASK_Y = 70
+ val DEFAULT_TASK_UPDATE =
+ TaskUpdate(
+ DEFAULT_TASK_ID,
+ DEFAULT_TASK_UID,
+ DEFAULT_TASK_HEIGHT,
+ DEFAULT_TASK_WIDTH,
+ DEFAULT_TASK_X,
+ DEFAULT_TASK_Y,
+ )
+
+ fun createTaskInfo(
+ windowMode: Int,
+ id: Int = DEFAULT_TASK_ID,
+ uid: Int = DEFAULT_TASK_UID,
+ taskHeight: Int = DEFAULT_TASK_HEIGHT,
+ taskWidth: Int = DEFAULT_TASK_WIDTH,
+ taskX: Int = DEFAULT_TASK_X,
+ taskY: Int = DEFAULT_TASK_Y,
+ ) =
+ ActivityManager.RunningTaskInfo().apply {
+ taskId = id
+ userId = uid
+ configuration.windowConfiguration.apply {
+ windowingMode = windowMode
+ positionInParent = Point(taskX, taskY)
+ bounds.set(Rect(taskX, taskY, taskX + taskWidth, taskY + taskHeight))
+ }
+ }
fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
val change =
- Change(
- WindowContainerToken(mock(IWindowContainerToken::class.java)),
- mock(SurfaceControl::class.java))
+ Change(WindowContainerToken(mock<IWindowContainerToken>()), mock<SurfaceControl>())
change.mode = mode
change.taskInfo = taskInfo
return change
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 310ccc2..6612aee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -119,54 +119,91 @@
}
@Test
- fun isOnlyActiveTask_noActiveTasks() {
- // Not an active task
- assertThat(repo.isOnlyActiveTask(1)).isFalse()
+ fun isOnlyVisibleNonClosingTask_noTasks() {
+ // No visible tasks
+ assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
+ assertThat(repo.isClosingTask(1)).isFalse()
}
@Test
- fun isOnlyActiveTask_singleActiveTask() {
- repo.addActiveTask(DEFAULT_DISPLAY, 1)
- // The only active task
- assertThat(repo.isActiveTask(1)).isTrue()
- assertThat(repo.isOnlyActiveTask(1)).isTrue()
- // Not an active task
- assertThat(repo.isActiveTask(99)).isFalse()
- assertThat(repo.isOnlyActiveTask(99)).isFalse()
+ fun isOnlyVisibleNonClosingTask_singleVisibleNonClosingTask() {
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+
+ // The only visible task
+ assertThat(repo.isVisibleTask(1)).isTrue()
+ assertThat(repo.isClosingTask(1)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(1)).isTrue()
+ // Not a visible task
+ assertThat(repo.isVisibleTask(99)).isFalse()
+ assertThat(repo.isClosingTask(99)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
}
@Test
- fun isOnlyActiveTask_multipleActiveTasks() {
- repo.addActiveTask(DEFAULT_DISPLAY, 1)
- repo.addActiveTask(DEFAULT_DISPLAY, 2)
+ fun isOnlyVisibleNonClosingTask_singleVisibleClosingTask() {
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.addClosingTask(DEFAULT_DISPLAY, 1)
+
+ // A visible task that's closing
+ assertThat(repo.isVisibleTask(1)).isTrue()
+ assertThat(repo.isClosingTask(1)).isTrue()
+ assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
+ // Not a visible task
+ assertThat(repo.isVisibleTask(99)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
+ }
+
+ @Test
+ fun isOnlyVisibleNonClosingTask_singleVisibleMinimizedTask() {
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.minimizeTask(DEFAULT_DISPLAY, 1)
+
+ // The visible task that's closing
+ assertThat(repo.isVisibleTask(1)).isTrue()
+ assertThat(repo.isMinimizedTask(1)).isTrue()
+ assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
+ // Not a visible task
+ assertThat(repo.isVisibleTask(99)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
+ }
+
+ @Test
+ fun isOnlyVisibleNonClosingTask_multipleVisibleNonClosingTasks() {
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+
// Not the only task
- assertThat(repo.isActiveTask(1)).isTrue()
- assertThat(repo.isOnlyActiveTask(1)).isFalse()
+ assertThat(repo.isVisibleTask(1)).isTrue()
+ assertThat(repo.isClosingTask(1)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
// Not the only task
- assertThat(repo.isActiveTask(2)).isTrue()
- assertThat(repo.isOnlyActiveTask(2)).isFalse()
- // Not an active task
- assertThat(repo.isActiveTask(99)).isFalse()
- assertThat(repo.isOnlyActiveTask(99)).isFalse()
+ assertThat(repo.isVisibleTask(2)).isTrue()
+ assertThat(repo.isClosingTask(2)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(2)).isFalse()
+ // Not a visible task
+ assertThat(repo.isVisibleTask(99)).isFalse()
+ assertThat(repo.isClosingTask(99)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
}
@Test
- fun isOnlyActiveTask_multipleDisplays() {
- repo.addActiveTask(DEFAULT_DISPLAY, 1)
- repo.addActiveTask(DEFAULT_DISPLAY, 2)
- repo.addActiveTask(SECOND_DISPLAY, 3)
+ fun isOnlyVisibleNonClosingTask_multipleDisplays() {
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
+ repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 2, visible = true)
+ repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 3, visible = true)
+
// Not the only task on DEFAULT_DISPLAY
- assertThat(repo.isActiveTask(1)).isTrue()
- assertThat(repo.isOnlyActiveTask(1)).isFalse()
+ assertThat(repo.isVisibleTask(1)).isTrue()
+ assertThat(repo.isOnlyVisibleNonClosingTask(1)).isFalse()
// Not the only task on DEFAULT_DISPLAY
- assertThat(repo.isActiveTask(2)).isTrue()
- assertThat(repo.isOnlyActiveTask(2)).isFalse()
- // The only active task on SECOND_DISPLAY
- assertThat(repo.isActiveTask(3)).isTrue()
- assertThat(repo.isOnlyActiveTask(3)).isTrue()
- // Not an active task
- assertThat(repo.isActiveTask(99)).isFalse()
- assertThat(repo.isOnlyActiveTask(99)).isFalse()
+ assertThat(repo.isVisibleTask(2)).isTrue()
+ assertThat(repo.isOnlyVisibleNonClosingTask(2)).isFalse()
+ // The only visible task on SECOND_DISPLAY
+ assertThat(repo.isVisibleTask(3)).isTrue()
+ assertThat(repo.isOnlyVisibleNonClosingTask(3)).isTrue()
+ // Not a visible task
+ assertThat(repo.isVisibleTask(99)).isFalse()
+ assertThat(repo.isOnlyVisibleNonClosingTask(99)).isFalse()
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index e17f7f2..0e53e10 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -18,6 +18,7 @@
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
+import android.app.KeyguardManager
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -45,6 +46,7 @@
import android.view.SurfaceControl
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
@@ -102,6 +104,8 @@
import java.util.Optional
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
@@ -146,6 +150,7 @@
@Mock lateinit var syncQueue: SyncTransactionQueue
@Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock lateinit var transitions: Transitions
+ @Mock lateinit var keyguardManager: KeyguardManager
@Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
@Mock
@@ -230,6 +235,7 @@
rootTaskDisplayAreaOrganizer,
dragAndDropController,
transitions,
+ keyguardManager,
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
toggleResizeDesktopTaskTransitionHandler,
@@ -436,14 +442,15 @@
}
@Test
- fun showDesktopApps_dontReorderMinimizedTask() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_desktopWallpaperDisabled_dontReorderMinimizedTask() {
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
val minimizedTask = setUpFreeformTask()
+
markTaskHidden(freeformTask)
markTaskHidden(minimizedTask)
desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
-
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
@@ -454,6 +461,26 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
+ setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ val minimizedTask = setUpFreeformTask()
+
+ markTaskHidden(freeformTask)
+ markTaskHidden(minimizedTask)
+ desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, minimizedTask.taskId)
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct = getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(2)
+ // Add desktop wallpaper activity
+ wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ // Reorder freeform task to top, don't reorder the minimized task
+ wct.assertReorderAt(index = 1, freeformTask, toTop = true)
+ }
+
+ @Test
fun getVisibleTaskCount_noTasks_returnsZero() {
assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
}
@@ -642,20 +669,37 @@
}
@Test
- fun moveToDesktop_nonRunningTask_launchesInFreeform() {
- whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
-
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
val task = createTaskInfo(1)
-
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
+
with(getLatestEnterDesktopWct()) {
assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
}
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
+ val task = createTaskInfo(1)
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+
+ controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
+
+ with(getLatestEnterDesktopWct()) {
+ // Add desktop wallpaper activity
+ assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+ // Launch task
+ assertLaunchTaskAt(index = 1, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
+ }
+
+ @Test
fun moveToDesktop_topActivityTranslucent_doesNothing() {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
val task =
@@ -773,21 +817,44 @@
}
@Test
- fun moveToDesktop_bringsTasksOverLimit_dontShowBackTask() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperDisabled_bringsTasksOver_dontShowBackTask() {
val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
- val homeTask = setUpHomeTask()
val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
val newTask = setUpFullscreenTask()
+ val homeTask = setUpHomeTask()
controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
val wct = getLatestEnterDesktopWct()
assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + home
wct.assertReorderAt(0, homeTask)
- for (i in 1..<taskLimit) { // Skipping freeformTasks[0]
- wct.assertReorderAt(index = i, task = freeformTasks[i])
- }
- wct.assertReorderAt(taskLimit, newTask)
+ wct.assertReorderSequenceInRange(
+ range = 1..<(taskLimit + 1),
+ *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+ newTask
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun moveToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
+ val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
+ val freeformTasks = (1..taskLimit).map { _ -> setUpFreeformTask() }
+ val newTask = setUpFullscreenTask()
+ setUpHomeTask()
+
+ controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + wallpaper
+ // Add desktop wallpaper activity
+ wct.assertPendingIntentAt(0, desktopWallpaperIntent)
+ wct.assertReorderSequenceInRange(
+ range = 1..<(taskLimit + 1),
+ *freeformTasks.drop(1).toTypedArray(), // Skipping freeformTasks[0]
+ newTask
+ )
}
@Test
@@ -923,7 +990,7 @@
@Test
fun onDesktopWindowClose_noActiveTasks() {
val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, 1 /* taskId */)
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = 1)
// Doesn't modify transaction
assertThat(wct.hierarchyOps).isEmpty()
}
@@ -932,7 +999,7 @@
fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() {
val task = setUpFreeformTask()
val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, task.taskId)
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
// Doesn't modify transaction
assertThat(wct.hierarchyOps).isEmpty()
}
@@ -944,12 +1011,38 @@
desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, task.taskId)
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
// Adds remove wallpaper operation
wct.assertRemoveAt(index = 0, wallpaperToken)
}
@Test
+ fun onDesktopWindowClose_singleActiveTask_isClosing() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
+ fun onDesktopWindowClose_singleActiveTask_isMinimized() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task.taskId)
+ // Doesn't modify transaction
+ assertThat(wct.hierarchyOps).isEmpty()
+ }
+
+ @Test
fun onDesktopWindowClose_multipleActiveTasks() {
val task1 = setUpFreeformTask()
setUpFreeformTask()
@@ -957,12 +1050,40 @@
desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
val wct = WindowContainerTransaction()
- controller.onDesktopWindowClose(wct, task1.taskId)
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
// Doesn't modify transaction
assertThat(wct.hierarchyOps).isEmpty()
}
@Test
+ fun onDesktopWindowClose_multipleActiveTasks_isOnlyNonClosingTask() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
+ // Adds remove wallpaper operation
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ fun onDesktopWindowClose_multipleActiveTasks_hasMinimized() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
+
+ val wct = WindowContainerTransaction()
+ controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
+ // Adds remove wallpaper operation
+ wct.assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -1052,41 +1173,106 @@
}
@Test
- fun handleRequest_freeformTask_freeformNotVisible_reorderedToTop() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val freeformTask1 = setUpFreeformTask()
- markTaskHidden(freeformTask1)
-
val freeformTask2 = createFreeformTask()
+
+ markTaskHidden(freeformTask1)
val result =
controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
- assertThat(result?.hierarchyOps?.size).isEqualTo(2)
- result!!.assertReorderAt(1, freeformTask2, toTop = true)
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ result.assertReorderAt(1, freeformTask2, toTop = true)
}
@Test
- fun handleRequest_freeformTask_noOtherTasks_reorderedToTop() {
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val freeformTask1 = setUpFreeformTask()
+ val freeformTask2 = createFreeformTask()
+
+ markTaskHidden(freeformTask1)
+ val result =
+ controller.handleRequest(Binder(), createTransition(freeformTask2, type = TRANSIT_TO_FRONT))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(3)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring active desktop tasks to front
+ result.assertReorderAt(1, freeformTask1, toTop = true)
+ // Bring new task to front
+ result.assertReorderAt(2, freeformTask2, toTop = true)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val task = createFreeformTask()
val result = controller.handleRequest(Binder(), createTransition(task))
- assertThat(result?.hierarchyOps?.size).isEqualTo(1)
- result!!.assertReorderAt(0, task, toTop = true)
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(1)
+ result.assertReorderAt(0, task, toTop = true)
}
@Test
- fun handleRequest_freeformTask_freeformOnOtherDisplayOnly_reorderedToTop() {
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val task = createFreeformTask()
+ val result = controller.handleRequest(Binder(), createTransition(task))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring new task to front
+ result.assertReorderAt(1, task, toTop = true)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
- val taskSecondDisplay = createFreeformTask(displayId = SECOND_DISPLAY)
+ // Second display task
+ createFreeformTask(displayId = SECOND_DISPLAY)
val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
- assertThat(result?.hierarchyOps?.size).isEqualTo(1)
- result!!.assertReorderAt(0, taskDefaultDisplay, toTop = true)
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(1)
+ result.assertReorderAt(0, taskDefaultDisplay, toTop = true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
+ // Second display task
+ createFreeformTask(displayId = SECOND_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
+
+ assertNotNull(result, "Should handle request")
+ assertThat(result.hierarchyOps?.size).isEqualTo(2)
+ // Add desktop wallpaper activity
+ result.assertPendingIntentAt(0, desktopWallpaperIntent)
+ // Bring new task to front
+ result.assertReorderAt(1, taskDefaultDisplay, toTop = true)
}
@Test
@@ -1118,6 +1304,17 @@
}
@Test
+ fun handleRequest_freeformTask_keyguardLocked_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
+ val freeformTask = createFreeformTask(displayId = DEFAULT_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+ assertNull(result, "Should NOT handle request")
+ }
+
+ @Test
fun handleRequest_notOpenOrToFrontTransition_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -1200,48 +1397,205 @@
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_backTransition_singleActiveTask_noToken() {
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleActiveTaskNoTokenFlagDisabled_doesNotHandle() {
val task = setUpFreeformTask()
+
val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
- // Doesn't handle request
- assertThat(result).isNull()
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_singleActiveTaskNoTokenFlagEnabled_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperDisabled() {
- desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
-
+ fun handleRequest_backTransition_singleActiveTaskWithTokenFlagDisabled_doesNotHandle() {
val task = setUpFreeformTask()
+
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
- // Doesn't handle request
- assertThat(result).isNull()
+
+ assertNull(result, "Should not handle request")
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperEnabled() {
+ fun handleRequest_backTransition_singleActiveTaskWithTokenFlagEnabled_handlesRequest() {
+ val task = setUpFreeformTask()
val wallpaperToken = MockToken().token()
- desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
- val task = setUpFreeformTask()
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
- assertThat(result).isNotNull()
- // Creates remove wallpaper transaction
- result!!.assertRemoveAt(index = 0, wallpaperToken)
+
+ assertNotNull(result, "Should handle request")
+ // Should create remove wallpaper transaction
+ .assertRemoveAt(index = 0, wallpaperToken)
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_backTransition_multipleActiveTasks() {
- desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
-
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleActiveTasksFlagDisabled_doesNotHandle() {
val task1 = setUpFreeformTask()
setUpFreeformTask()
+
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
- // Doesn't handle request
- assertThat(result).isNull()
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleActiveTasksFlagEnabled_doesNotHandle() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+ val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleActiveTasksSingleNonClosing_handlesRequest() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+ assertNotNull(result, "Should handle request")
+ // Should create remove wallpaper transaction
+ .assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_backTransition_multipleActiveTasksSingleNonMinimized_handlesRequest() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+
+ assertNotNull(result, "Should handle request")
+ // Should create remove wallpaper transaction
+ .assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleActiveTaskNoTokenFlagDisabled_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleActiveTaskNoTokenFlagEnabled_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleActiveTaskWithTokenFlagDisabled_doesNotHandle() {
+ val task = setUpFreeformTask()
+
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+ val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_singleActiveTaskWithTokenFlagEnabled_handlesRequest() {
+ val task = setUpFreeformTask()
+ val wallpaperToken = MockToken().token()
+
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ assertNotNull(result, "Should handle request")
+ // Should create remove wallpaper transaction
+ .assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleActiveTasksFlagDisabled_doesNotHandle() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+ val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleActiveTasksFlagEnabled_doesNotHandle() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+ val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ assertNull(result, "Should not handle request")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleActiveTasksSingleNonClosing_handlesRequest() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ assertNotNull(result, "Should handle request")
+ // Should create remove wallpaper transaction
+ .assertRemoveAt(index = 0, wallpaperToken)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun handleRequest_closeTransition_multipleActiveTasksSingleNonMinimized_handlesRequest() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val wallpaperToken = MockToken().token()
+
+ desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+ desktopModeTaskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
+
+ assertNotNull(result, "Should handle request")
+ // Should create remove wallpaper transaction
+ .assertRemoveAt(index = 0, wallpaperToken)
}
@Test
@@ -1615,6 +1969,7 @@
task.topActivityInfo = activityInfo
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
desktopModeTaskRepository.addActiveTask(displayId, task.taskId)
+ desktopModeTaskRepository.updateVisibleFreeformTasks(displayId, task.taskId, visible = true)
desktopModeTaskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
runningTasks.add(task)
return task
@@ -1826,6 +2181,16 @@
}
}
+/** Checks if the reorder hierarchy operations in [range] correspond to [tasks] list */
+private fun WindowContainerTransaction.assertReorderSequenceInRange(
+ range: IntRange,
+ vararg tasks: RunningTaskInfo
+) {
+ assertThat(hierarchyOps.slice(range).map { it.type to it.container })
+ .containsExactlyElementsIn(tasks.map { HIERARCHY_OP_TYPE_REORDER to it.token.asBinder() })
+ .inOrder()
+}
+
private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
assertIndexInBounds(index)
val op = hierarchyOps[index]
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 5880ffb..72950a8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -88,8 +88,11 @@
@Test
public void getAnimator_withBounds_returnBoundsAnimator() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
+ final Rect startValue = new Rect(0, 0, 100, 100);
+ final Rect endValue1 = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
+ .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 974539f..aa2d6f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -241,16 +241,16 @@
@Test
public void updateGravity_move_expanded_valid() {
- mTvPipBoundsState.setTvPipExpanded(true);
-
// Vertical expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.CENTER_VERTICAL | Gravity.LEFT, true);
moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, true);
// Horizontal expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.TOP | Gravity.CENTER_HORIZONTAL, true);
moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, true);
@@ -281,10 +281,9 @@
@Test
public void updateGravity_move_expanded_invalid() {
- mTvPipBoundsState.setTvPipExpanded(true);
-
// Vertical expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(VERTICAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
moveAndCheckGravity(KEYCODE_DPAD_RIGHT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, false);
moveAndCheckGravity(KEYCODE_DPAD_UP, Gravity.CENTER_VERTICAL | Gravity.RIGHT, false);
@@ -297,6 +296,7 @@
// Horizontal expanded PiP.
mTvPipBoundsState.setDesiredTvExpandedAspectRatio(HORIZONTAL_EXPANDED_ASPECT_RATIO, true);
+ mTvPipBoundsState.setTvPipExpanded(true);
mTvPipBoundsState.setTvPipGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
moveAndCheckGravity(KEYCODE_DPAD_DOWN, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, false);
moveAndCheckGravity(KEYCODE_DPAD_LEFT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
index bbd65be..15b73c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.recents
import android.app.ActivityManager
+import android.app.ActivityManager.RecentTaskInfo
import android.graphics.Rect
import android.os.Parcel
import android.testing.AndroidTestingRunner
@@ -33,6 +34,7 @@
import com.android.wm.shell.util.SplitBounds
import com.google.common.truth.Correspondence
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -86,12 +88,13 @@
@Test
fun testFreeformTasks_hasCorrectType() {
- assertThat(freeformTasksGroupInfo().type).isEqualTo(TYPE_FREEFORM)
+ assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).type)
+ .isEqualTo(TYPE_FREEFORM)
}
@Test
- fun testSplitTasks_taskInfoList_hasThreeTasks() {
- val list = freeformTasksGroupInfo().taskInfoList
+ fun testCreateFreeformTasks_hasCorrectNumberOfTasks() {
+ val list = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList
assertThat(list).hasSize(3)
assertThat(list[0].taskId).isEqualTo(1)
assertThat(list[1].taskId).isEqualTo(2)
@@ -99,6 +102,16 @@
}
@Test
+ fun testCreateFreeformTasks_nonExistentMinimizedTaskId_throwsException() {
+ assertThrows(IllegalArgumentException::class.java) {
+ freeformTasksGroupInfo(
+ freeformTaskIds = arrayOf(1, 2, 3),
+ minimizedTaskIds = arrayOf(1, 4)
+ )
+ }
+ }
+
+ @Test
fun testParcelling_singleTask() {
val recentTaskInfo = singleTaskGroupInfo()
val parcel = Parcel.obtain()
@@ -129,7 +142,7 @@
@Test
fun testParcelling_freeformTasks() {
- val recentTaskInfo = freeformTasksGroupInfo()
+ val recentTaskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3))
val parcel = Parcel.obtain()
recentTaskInfo.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
@@ -145,6 +158,21 @@
.containsExactly(1, 2, 3)
}
+ @Test
+ fun testParcelling_freeformTasks_minimizedTasks() {
+ val recentTaskInfo = freeformTasksGroupInfo(
+ freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(2))
+
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
+ assertThat(recentTaskInfoParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
+ }
+
private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
taskId = id
token = WindowContainerToken(mock(IWindowContainerToken::class.java))
@@ -162,10 +190,12 @@
return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
}
- private fun freeformTasksGroupInfo(): GroupedRecentTaskInfo {
- val task1 = createTaskInfo(id = 1)
- val task2 = createTaskInfo(id = 2)
- val task3 = createTaskInfo(id = 3)
- return GroupedRecentTaskInfo.forFreeformTasks(task1, task2, task3)
+ private fun freeformTasksGroupInfo(
+ freeformTaskIds: Array<Int>,
+ minimizedTaskIds: Array<Int> = emptyArray()
+ ): GroupedRecentTaskInfo {
+ return GroupedRecentTaskInfo.forFreeformTasks(
+ freeformTaskIds.map { createTaskInfo(it) }.toTypedArray(),
+ minimizedTaskIds.toSet())
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index e291c0e..5c5a1a2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -399,7 +399,7 @@
}
@Test
- public void testGetRecentTasks_proto2Enabled_ignoresMinimizedFreeformTasks() {
+ public void testGetRecentTasks_proto2Enabled_includesMinimizedFreeformTasks() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
@@ -415,8 +415,7 @@
ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
- // 2 freeform tasks should be grouped into one, 1 task should be skipped, 3 total recents
- // entries
+ // 3 freeform tasks should be grouped into one, 2 single tasks, 3 total recents entries
assertEquals(3, recentTasks.size());
GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
@@ -428,9 +427,10 @@
assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
// Check freeform group entries
- assertEquals(2, freeformGroup.getTaskInfoList().size());
+ assertEquals(3, freeformGroup.getTaskInfoList().size());
assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
- assertEquals(t5, freeformGroup.getTaskInfoList().get(1));
+ assertEquals(t3, freeformGroup.getTaskInfoList().get(1));
+ assertEquals(t5, freeformGroup.getTaskInfoList().get(2));
// Check single entries
assertEquals(t2, singleGroup1.getTaskInfo1());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
index f959970..0e5efa6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -48,7 +48,6 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-
/**
* Test class for {@link TaskStackTransitionObserver}
*
@@ -168,6 +167,80 @@
.isEqualTo(freeformOpenChange.taskInfo?.windowingMode)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun transitionMerged_withChange_onlyOpenChangeIsNotified() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Create open transition
+ val change =
+ createChange(
+ WindowManager.TRANSIT_OPEN,
+ createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+
+ // create change transition to be merged to above transition
+ val mergedChange =
+ createChange(
+ WindowManager.TRANSIT_CHANGE,
+ createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val mergedTransitionInfo =
+ TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0).addChange(mergedChange).build()
+ val mergedTransition = Mockito.mock(IBinder::class.java)
+
+ callOnTransitionReady(transitionInfo)
+ callOnTransitionReady(mergedTransitionInfo, mergedTransition)
+ callOnTransitionMerged(mergedTransition)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId)
+ assertThat(listener.taskInfoToBeNotified.windowingMode)
+ .isEqualTo(change.taskInfo?.windowingMode)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun transitionMerged_withOpen_lastOpenChangeIsNotified() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+ // Create open transition
+ val change =
+ createChange(
+ WindowManager.TRANSIT_OPEN,
+ createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+
+ // create change transition to be merged to above transition
+ val mergedChange =
+ createChange(
+ WindowManager.TRANSIT_OPEN,
+ createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val mergedTransitionInfo =
+ TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(mergedChange).build()
+ val mergedTransition = Mockito.mock(IBinder::class.java)
+
+ callOnTransitionReady(transitionInfo)
+ callOnTransitionReady(mergedTransitionInfo, mergedTransition)
+ callOnTransitionMerged(mergedTransition)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(mergedChange.taskInfo?.taskId)
+ assertThat(listener.taskInfoToBeNotified.windowingMode)
+ .isEqualTo(mergedChange.taskInfo?.windowingMode)
+ }
+
class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener {
var taskInfoToBeNotified = ActivityManager.RunningTaskInfo()
@@ -179,11 +252,14 @@
}
/** Simulate calling the onTransitionReady() method */
- private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
+ private fun callOnTransitionReady(
+ transitionInfo: TransitionInfo,
+ transition: IBinder = mockTransitionBinder
+ ) {
val startT = Mockito.mock(SurfaceControl.Transaction::class.java)
val finishT = Mockito.mock(SurfaceControl.Transaction::class.java)
- transitionObserver.onTransitionReady(mockTransitionBinder, transitionInfo, startT, finishT)
+ transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
}
/** Simulate calling the onTransitionFinished() method */
@@ -191,6 +267,11 @@
transitionObserver.onTransitionFinished(mockTransitionBinder, false)
}
+ /** Simulate calling the onTransitionMerged() method */
+ private fun callOnTransitionMerged(merged: IBinder, playing: IBinder = mockTransitionBinder) {
+ transitionObserver.onTransitionMerged(merged, playing)
+ }
+
companion object {
fun createTaskInfo(taskId: Int, windowingMode: Int): ActivityManager.RunningTaskInfo {
val taskInfo = ActivityManager.RunningTaskInfo()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ChangeBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ChangeBuilder.java
new file mode 100644
index 0000000..b54c3bf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ChangeBuilder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+
+public class ChangeBuilder {
+ final TransitionInfo.Change mChange;
+
+ ChangeBuilder(@WindowManager.TransitionType int mode) {
+ mChange = new TransitionInfo.Change(null /* token */, createMockSurface(true));
+ mChange.setMode(mode);
+ }
+
+ ChangeBuilder setFlags(@TransitionInfo.ChangeFlags int flags) {
+ mChange.setFlags(flags);
+ return this;
+ }
+
+ ChangeBuilder setTask(RunningTaskInfo taskInfo) {
+ mChange.setTaskInfo(taskInfo);
+ return this;
+ }
+
+ ChangeBuilder setRotate(int anim) {
+ return setRotate(Surface.ROTATION_90, anim);
+ }
+
+ ChangeBuilder setRotate() {
+ return setRotate(ROTATION_ANIMATION_UNSPECIFIED);
+ }
+
+ ChangeBuilder setRotate(@Surface.Rotation int target, int anim) {
+ mChange.setRotation(Surface.ROTATION_0, target);
+ mChange.setRotationAnimation(anim);
+ return this;
+ }
+
+ TransitionInfo.Change build() {
+ return mChange;
+ }
+
+ private static SurfaceControl createMockSurface(boolean valid) {
+ SurfaceControl sc = mock(SurfaceControl.class);
+ doReturn(valid).when(sc).isValid();
+ return sc;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
new file mode 100644
index 0000000..754a173
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_SLEEP;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_SYNC;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sysui.ShellInit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the default animation handler that is used if no other special-purpose handler picks
+ * up an animation request.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:DefaultTransitionHandlerTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DefaultTransitionHandlerTest extends ShellTestCase {
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ private final DisplayController mDisplayController = mock(DisplayController.class);
+ private final TransactionPool mTransactionPool = new MockTransactionPool();
+ private final TestShellExecutor mMainExecutor = new TestShellExecutor();
+ private final TestShellExecutor mAnimExecutor = new TestShellExecutor();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+ private ShellInit mShellInit;
+ private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private DefaultTransitionHandler mTransitionHandler;
+
+ @Before
+ public void setUp() {
+ mShellInit = new ShellInit(mMainExecutor);
+ mRootTaskDisplayAreaOrganizer = new RootTaskDisplayAreaOrganizer(
+ mMainExecutor,
+ mContext,
+ mShellInit);
+ mTransitionHandler = new DefaultTransitionHandler(
+ mContext, mShellInit, mDisplayController,
+ mTransactionPool, mMainExecutor, mMainHandler, mAnimExecutor,
+ mRootTaskDisplayAreaOrganizer);
+ mShellInit.init();
+ }
+
+ @After
+ public void tearDown() {
+ flushHandlers();
+ }
+
+ private void flushHandlers() {
+ mMainHandler.runWithScissors(() -> {
+ mAnimExecutor.flushAll();
+ mMainExecutor.flushAll();
+ }, 1000L);
+ }
+
+ @Test
+ public void testAnimationBackgroundCreatedForTaskTransition() {
+ final TransitionInfo.Change openTask = new ChangeBuilder(TRANSIT_OPEN)
+ .setTask(createTaskInfo(1))
+ .build();
+ final TransitionInfo.Change closeTask = new ChangeBuilder(TRANSIT_TO_BACK)
+ .setTask(createTaskInfo(2))
+ .build();
+
+ final IBinder token = new Binder();
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(openTask)
+ .addChange(closeTask)
+ .build();
+ final SurfaceControl.Transaction startT = MockTransactionPool.create();
+ final SurfaceControl.Transaction finishT = MockTransactionPool.create();
+
+ mTransitionHandler.startAnimation(token, info, startT, finishT,
+ mock(Transitions.TransitionFinishCallback.class));
+
+ mergeSync(mTransitionHandler, token);
+ flushHandlers();
+
+ verify(startT).setColor(any(), any());
+ }
+
+ @Test
+ public void testNoAnimationBackgroundForTranslucentTasks() {
+ final TransitionInfo.Change openTask = new ChangeBuilder(TRANSIT_OPEN)
+ .setTask(createTaskInfo(1))
+ .setFlags(FLAG_TRANSLUCENT)
+ .build();
+ final TransitionInfo.Change closeTask = new ChangeBuilder(TRANSIT_TO_BACK)
+ .setTask(createTaskInfo(2))
+ .setFlags(FLAG_TRANSLUCENT)
+ .build();
+
+ final IBinder token = new Binder();
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(openTask)
+ .addChange(closeTask)
+ .build();
+ final SurfaceControl.Transaction startT = MockTransactionPool.create();
+ final SurfaceControl.Transaction finishT = MockTransactionPool.create();
+
+ mTransitionHandler.startAnimation(token, info, startT, finishT,
+ mock(Transitions.TransitionFinishCallback.class));
+
+ mergeSync(mTransitionHandler, token);
+ flushHandlers();
+
+ verify(startT, never()).setColor(any(), any());
+ }
+
+ @Test
+ public void testNoAnimationBackgroundForWallpapers() {
+ final TransitionInfo.Change openWallpaper = new ChangeBuilder(TRANSIT_OPEN)
+ .setFlags(TransitionInfo.FLAG_IS_WALLPAPER)
+ .build();
+ final TransitionInfo.Change closeWallpaper = new ChangeBuilder(TRANSIT_TO_BACK)
+ .setFlags(TransitionInfo.FLAG_IS_WALLPAPER)
+ .build();
+
+ final IBinder token = new Binder();
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(openWallpaper)
+ .addChange(closeWallpaper)
+ .build();
+ final SurfaceControl.Transaction startT = MockTransactionPool.create();
+ final SurfaceControl.Transaction finishT = MockTransactionPool.create();
+
+ mTransitionHandler.startAnimation(token, info, startT, finishT,
+ mock(Transitions.TransitionFinishCallback.class));
+
+ mergeSync(mTransitionHandler, token);
+ flushHandlers();
+
+ verify(startT, never()).setColor(any(), any());
+ }
+
+ private static void mergeSync(Transitions.TransitionHandler handler, IBinder token) {
+ handler.mergeAnimation(
+ new Binder(),
+ new TransitionInfoBuilder(TRANSIT_SLEEP, FLAG_SYNC).build(),
+ MockTransactionPool.create(),
+ token,
+ mock(Transitions.TransitionFinishCallback.class));
+ }
+
+ private static RunningTaskInfo createTaskInfo(int taskId) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.topActivityType = ACTIVITY_TYPE_STANDARD;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ taskInfo.configuration.windowConfiguration.setActivityType(taskInfo.topActivityType);
+ taskInfo.token = mock(WindowContainerToken.class);
+ return taskInfo;
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/MockTransactionPool.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/MockTransactionPool.java
new file mode 100644
index 0000000..574a87a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/MockTransactionPool.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static org.mockito.Mockito.RETURNS_SELF;
+import static org.mockito.Mockito.mock;
+
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.util.StubTransaction;
+
+public class MockTransactionPool extends TransactionPool {
+
+ public static SurfaceControl.Transaction create() {
+ return mock(StubTransaction.class, RETURNS_SELF);
+ }
+
+ @Override
+ public SurfaceControl.Transaction acquire() {
+ return create();
+ }
+
+ @Override
+ public void release(SurfaceControl.Transaction t) {
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 964d86e..8331d59 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -79,7 +79,6 @@
import android.view.IRecentsAnimationRunner;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.IWindowContainerToken;
@@ -1192,7 +1191,8 @@
mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
final RecentsTransitionHandler recentsHandler =
new RecentsTransitionHandler(shellInit, transitions,
- mock(RecentTasksController.class), mock(HomeTransitionObserver.class));
+ mock(RecentTasksController.class), mock(HomeTransitionObserver.class),
+ () -> mock(SurfaceControl.Transaction.class));
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
shellInit.init();
@@ -1614,43 +1614,6 @@
eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean());
}
- class ChangeBuilder {
- final TransitionInfo.Change mChange;
-
- ChangeBuilder(@WindowManager.TransitionType int mode) {
- mChange = new TransitionInfo.Change(null /* token */, createMockSurface(true));
- mChange.setMode(mode);
- }
-
- ChangeBuilder setFlags(@TransitionInfo.ChangeFlags int flags) {
- mChange.setFlags(flags);
- return this;
- }
-
- ChangeBuilder setTask(RunningTaskInfo taskInfo) {
- mChange.setTaskInfo(taskInfo);
- return this;
- }
-
- ChangeBuilder setRotate(int anim) {
- return setRotate(Surface.ROTATION_90, anim);
- }
-
- ChangeBuilder setRotate() {
- return setRotate(ROTATION_ANIMATION_UNSPECIFIED);
- }
-
- ChangeBuilder setRotate(@Surface.Rotation int target, int anim) {
- mChange.setRotation(Surface.ROTATION_0, target);
- mChange.setRotationAnimation(anim);
- return this;
- }
-
- TransitionInfo.Change build() {
- return mChange;
- }
- }
-
class TestTransitionHandler implements Transitions.TransitionHandler {
ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> mFinishes =
new ArrayList<>();
@@ -1739,12 +1702,6 @@
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
}
- private static SurfaceControl createMockSurface(boolean valid) {
- SurfaceControl sc = mock(SurfaceControl.class);
- doReturn(valid).when(sc).isValid();
- return sc;
- }
-
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode, int activityType) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index f750e6b..86aded7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -21,7 +21,9 @@
import android.graphics.PointF
import android.graphics.Rect
import android.os.IBinder
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display
import android.window.WindowContainerToken
@@ -36,6 +38,7 @@
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -53,21 +56,32 @@
class DragPositioningCallbackUtilityTest {
@Mock
private lateinit var mockWindowDecoration: WindowDecoration<*>
+
@Mock
private lateinit var taskToken: WindowContainerToken
+
@Mock
private lateinit var taskBinder: IBinder
+
@Mock
private lateinit var mockDisplayController: DisplayController
+
@Mock
private lateinit var mockDisplayLayout: DisplayLayout
+
@Mock
private lateinit var mockDisplay: Display
+
@Mock
private lateinit var mockContext: Context
+
@Mock
private lateinit var mockResources: Resources
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule()
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -323,6 +337,49 @@
assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 50)
}
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
+ fun testChangeBounds_windowSizeExceedsStableBounds_shouldBeAllowedToChangeBounds() {
+ val startingPoint =
+ PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
+ OFF_CENTER_STARTING_BOUNDS.bottom.toFloat())
+ val repositionTaskBounds = Rect(OFF_CENTER_STARTING_BOUNDS)
+ // Increase height and width by STABLE_BOUNDS. Subtract by 5px so that it doesn't reach
+ // the disallowed drag area.
+ val offset = 5
+ val newX = STABLE_BOUNDS.right.toFloat() - offset
+ val newY = STABLE_BOUNDS.bottom.toFloat() - offset
+ val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+ DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ repositionTaskBounds, OFF_CENTER_STARTING_BOUNDS, STABLE_BOUNDS, delta,
+ mockDisplayController, mockWindowDecoration)
+ assertThat(repositionTaskBounds.width()).isGreaterThan(STABLE_BOUNDS.right)
+ assertThat(repositionTaskBounds.height()).isGreaterThan(STABLE_BOUNDS.bottom)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
+ fun testChangeBoundsInDesktopMode_windowSizeExceedsStableBounds_shouldBeLimitedToDisplaySize() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+ val startingPoint =
+ PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
+ OFF_CENTER_STARTING_BOUNDS.bottom.toFloat())
+ val repositionTaskBounds = Rect(OFF_CENTER_STARTING_BOUNDS)
+ // Increase height and width by STABLE_BOUNDS. Subtract by 5px so that it doesn't reach
+ // the disallowed drag area.
+ val offset = 5
+ val newX = STABLE_BOUNDS.right.toFloat() - offset
+ val newY = STABLE_BOUNDS.bottom.toFloat() - offset
+ val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+ DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ repositionTaskBounds, OFF_CENTER_STARTING_BOUNDS, STABLE_BOUNDS, delta,
+ mockDisplayController, mockWindowDecoration)
+ assertThat(repositionTaskBounds.width()).isLessThan(STABLE_BOUNDS.right)
+ assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom)
+ }
+
private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) {
mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
taskId = TASK_ID
@@ -347,6 +404,7 @@
private const val NAVBAR_HEIGHT = 50
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10)
private val DISALLOWED_RESIZE_AREA = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 48ac1e5..901ca90 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -17,6 +17,8 @@
import android.app.ActivityManager
import android.app.WindowConfiguration
+import android.content.Context
+import android.content.res.Resources
import android.graphics.Point
import android.graphics.Rect
import android.os.IBinder
@@ -98,6 +100,10 @@
private lateinit var mockFinishCallback: TransitionFinishCallback
@Mock
private lateinit var mockTransitions: Transitions
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var mockResources: Resources
private lateinit var taskPositioner: VeiledResizeTaskPositioner
@@ -105,6 +111,9 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mockDesktopWindowDecoration.mDisplay = mockDisplay
+ mockDesktopWindowDecoration.mDecorWindowContext = mockContext
+ whenever(mockContext.getResources()).thenReturn(mockResources)
whenever(taskToken.asBinder()).thenReturn(taskBinder)
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 77800a3..2fff4f5 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -212,6 +212,7 @@
"tests/AttributeResolution_test.cpp",
"tests/BigBuffer_test.cpp",
"tests/ByteBucketArray_test.cpp",
+ "tests/CombinedIterator_test.cpp",
"tests/Config_test.cpp",
"tests/ConfigDescription_test.cpp",
"tests/ConfigLocale_test.cpp",
@@ -267,6 +268,7 @@
cc_benchmark {
name: "libandroidfw_benchmarks",
defaults: ["libandroidfw_defaults"],
+ test_config: "tests/AndroidTest_Benchmarks.xml",
srcs: [
// Helpers/infra for benchmarking.
"tests/BenchMain.cpp",
@@ -282,7 +284,11 @@
"tests/Theme_bench.cpp",
],
shared_libs: common_test_libs,
- data: ["tests/data/**/*.apk"],
+ data: [
+ "tests/data/**/*.apk",
+ ":FrameworkResourcesSparseTestApp",
+ ":FrameworkResourcesNotSparseTestApp",
+ ],
}
cc_library {
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 46f636e..822a387 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -23,9 +23,11 @@
#include <map>
#include <set>
#include <span>
+#include <utility>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/CombinedIterator.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
#include "androidfw/Util.h"
@@ -1622,6 +1624,12 @@
Theme::~Theme() = default;
+static bool IsUndefined(const Res_value& value) {
+ // DATA_NULL_EMPTY (@empty) is a valid resource value and DATA_NULL_UNDEFINED represents
+ // an absence of a valid value.
+ return value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY;
+}
+
base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
ATRACE_NAME("Theme::ApplyStyle");
@@ -1633,39 +1641,76 @@
// Merge the flags from this style.
type_spec_flags_ |= (*bag)->type_spec_flags;
+ //
+ // This function is the most expensive part of applying an frro to the existing app resources,
+ // and needs to be as efficient as possible.
+ // The data structure we're working with is two parallel sorted arrays of keys (resource IDs)
+ // and entries (resource value + some attributes).
+ // The styles get applied in sequence, starting with an empty set of attributes. Each style
+ // contains its values for the theme attributes, and gets applied in either normal or forced way:
+ // - normal way never overrides the existing attribute, so only unique style attributes are added
+ // - forced way overrides anything for that attribute, and if it's undefined it removes the
+ // previous value completely
+ //
+ // Style attributes come in a Bag data type - a sorted array of attributes with their values. This
+ // means we don't need to re-sort the attributes ever, and instead:
+ // - for an already existing attribute just skip it or apply the forced value
+ // - if the forced value is undefined, mark it undefined as well to get rid of it later
+ // - for a new attribute append it to the array, forming a new sorted section of new attributes
+ // past the end of the original ones (ignore undefined ones here)
+ // - inplace merge two sorted sections to form a single sorted array again.
+ // - run the last pass to remove all undefined elements
+ //
+ // Using this algorithm performs better than a repeated binary search + insert in the middle,
+ // as that keeps shifting the tail end of the arrays and wasting CPU cycles in memcpy().
+ //
+ const auto starting_size = keys_.size();
+ if (starting_size == 0) {
+ keys_.reserve((*bag)->entry_count);
+ entries_.reserve((*bag)->entry_count);
+ }
+ bool wrote_undefined = false;
for (auto it = begin(*bag); it != end(*bag); ++it) {
const uint32_t attr_res_id = it->key;
-
// If the resource ID passed in is not a style, the key can be some other identifier that is not
// a resource ID. We should fail fast instead of operating with strange resource IDs.
if (!is_valid_resid(attr_res_id)) {
return base::unexpected(std::nullopt);
}
-
- // DATA_NULL_EMPTY (@empty) is a valid resource value and DATA_NULL_UNDEFINED represents
- // an absence of a valid value.
- bool is_undefined = it->value.dataType == Res_value::TYPE_NULL &&
- it->value.data != Res_value::DATA_NULL_EMPTY;
+ const bool is_undefined = IsUndefined(it->value);
if (!force && is_undefined) {
continue;
}
-
- const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), attr_res_id);
- const auto entry_it = entries_.begin() + (key_it - keys_.begin());
- if (key_it != keys_.end() && *key_it == attr_res_id) {
- if (is_undefined) {
- // DATA_NULL_UNDEFINED clears the value of the attribute in the theme only when `force` is
- // true.
- keys_.erase(key_it);
- entries_.erase(entry_it);
- } else if (force) {
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.begin() + starting_size, attr_res_id);
+ if (key_it != keys_.begin() + starting_size && *key_it == attr_res_id) {
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
+ if (force || IsUndefined(entry_it->value)) {
*entry_it = Entry{it->cookie, (*bag)->type_spec_flags, it->value};
+ wrote_undefined |= is_undefined;
}
- } else {
- keys_.insert(key_it, attr_res_id);
- entries_.insert(entry_it, Entry{it->cookie, (*bag)->type_spec_flags, it->value});
+ } else if (!is_undefined) {
+ keys_.emplace_back(attr_res_id);
+ entries_.emplace_back(it->cookie, (*bag)->type_spec_flags, it->value);
}
}
+
+ if (starting_size && keys_.size() != starting_size) {
+ std::inplace_merge(
+ CombinedIterator(keys_.begin(), entries_.begin()),
+ CombinedIterator(keys_.begin() + starting_size, entries_.begin() + starting_size),
+ CombinedIterator(keys_.end(), entries_.end()));
+ }
+ if (wrote_undefined) {
+ auto new_end = std::remove_if(CombinedIterator(keys_.begin(), entries_.begin()),
+ CombinedIterator(keys_.end(), entries_.end()),
+ [](const auto& pair) { return IsUndefined(pair.second.value); });
+ keys_.erase(new_end.it1, keys_.end());
+ entries_.erase(new_end.it2, entries_.end());
+ }
+ if (android::base::kEnableDChecks && !std::is_sorted(keys_.begin(), keys_.end())) {
+ ALOGW("Bag %u was unsorted in the apk?", unsigned(resid));
+ return base::unexpected(std::nullopt);
+ }
return {};
}
@@ -1691,6 +1736,9 @@
return std::nullopt;
}
const auto entry_it = entries_.begin() + (key_it - keys_.begin());
+ if (IsUndefined(entry_it->value)) {
+ return std::nullopt;
+ }
type_spec_flags |= entry_it->type_spec_flags;
if (entry_it->value.dataType == Res_value::TYPE_ATTRIBUTE) {
resid = entry_it->value.data;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 17a8ba6..ac46bc5 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -280,9 +280,9 @@
private:
SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie,
- uint32_t type_flags, uint32_t resid, const ResTable_config& config) :
+ uint32_t type_flags, uint32_t resid, ResTable_config config) :
cookie(cookie), data(value_data), type(value_type), flags(type_flags),
- resid(resid), config(config) {};
+ resid(resid), config(std::move(config)) {}
};
// Retrieves the best matching resource value with ID `resid`.
diff --git a/libs/androidfw/include/androidfw/CombinedIterator.h b/libs/androidfw/include/androidfw/CombinedIterator.h
new file mode 100644
index 0000000..4ff6a7d
--- /dev/null
+++ b/libs/androidfw/include/androidfw/CombinedIterator.h
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <compare>
+#include <iterator>
+#include <utility>
+
+namespace android {
+
+namespace detail {
+// A few useful aliases to not repeat them everywhere
+template <class It1, class It2>
+using Value = std::pair<typename std::iterator_traits<It1>::value_type,
+ typename std::iterator_traits<It2>::value_type>;
+
+template <class It1, class It2>
+using BaseRefPair = std::pair<typename std::iterator_traits<It1>::reference,
+ typename std::iterator_traits<It2>::reference>;
+
+template <class It1, class It2>
+struct RefPair : BaseRefPair<It1, It2> {
+ using Base = BaseRefPair<It1, It2>;
+ using Value = detail::Value<It1, It2>;
+
+ RefPair(It1 it1, It2 it2) : Base(*it1, *it2) {
+ }
+
+ RefPair& operator=(const Value& v) {
+ this->first = v.first;
+ this->second = v.second;
+ return *this;
+ }
+ operator Value() const {
+ return Value(this->first, this->second);
+ }
+ bool operator==(const RefPair& other) {
+ return this->first == other.first;
+ }
+ bool operator==(const Value& other) {
+ return this->first == other.first;
+ }
+ std::strong_ordering operator<=>(const RefPair& other) const {
+ return this->first <=> other.first;
+ }
+ std::strong_ordering operator<=>(const Value& other) const {
+ return this->first <=> other.first;
+ }
+ friend void swap(RefPair& l, RefPair& r) {
+ using std::swap;
+ swap(l.first, r.first);
+ swap(l.second, r.second);
+ }
+};
+
+template <class It1, class It2>
+struct RefPairPtr {
+ RefPair<It1, It2> value;
+
+ RefPair<It1, It2>* operator->() const {
+ return &value;
+ }
+};
+} // namespace detail
+
+//
+// CombinedIterator - a class to combine two iterators to process them as a single iterator to a
+// pair of values. Useful for processing a data structure of "struct of arrays", replacing
+// array of structs for cache locality.
+//
+// The value type is a pair of copies of the values of each iterator, and the reference is a
+// pair of references to the corresponding values. Comparison only compares the first element,
+// making it most useful for using on data like (vector<Key>, vector<Value>) for binary searching,
+// sorting both together and so on.
+//
+// The class is designed for handling arrays, so it requires random access iterators as an input.
+//
+
+template <class It1, class It2>
+requires std::random_access_iterator<It1> && std::random_access_iterator<It2>
+struct CombinedIterator {
+ typedef detail::Value<It1, It2> value_type;
+ typedef detail::RefPair<It1, It2> reference;
+ typedef std::ptrdiff_t difference_type;
+ typedef detail::RefPairPtr<It1, It2> pointer;
+ typedef std::random_access_iterator_tag iterator_category;
+
+ CombinedIterator(It1 it1 = {}, It2 it2 = {}) : it1(it1), it2(it2) {
+ }
+
+ bool operator<(const CombinedIterator& other) const {
+ return it1 < other.it1;
+ }
+ bool operator<=(const CombinedIterator& other) const {
+ return it1 <= other.it1;
+ }
+ bool operator>(const CombinedIterator& other) const {
+ return it1 > other.it1;
+ }
+ bool operator>=(const CombinedIterator& other) const {
+ return it1 >= other.it1;
+ }
+ bool operator==(const CombinedIterator& other) const {
+ return it1 == other.it1;
+ }
+ pointer operator->() const {
+ return pointer{{it1, it2}};
+ }
+ reference operator*() const {
+ return {it1, it2};
+ }
+ reference operator[](difference_type n) const {
+ return {it1 + n, it2 + n};
+ }
+
+ CombinedIterator& operator++() {
+ ++it1;
+ ++it2;
+ return *this;
+ }
+ CombinedIterator operator++(int) {
+ const auto res = *this;
+ ++*this;
+ return res;
+ }
+ CombinedIterator& operator--() {
+ --it1;
+ --it2;
+ return *this;
+ }
+ CombinedIterator operator--(int) {
+ const auto res = *this;
+ --*this;
+ return res;
+ }
+ CombinedIterator& operator+=(difference_type n) {
+ it1 += n;
+ it2 += n;
+ return *this;
+ }
+ CombinedIterator operator+(difference_type n) const {
+ CombinedIterator res = *this;
+ return res += n;
+ }
+
+ CombinedIterator& operator-=(difference_type n) {
+ it1 -= n;
+ it2 -= n;
+ return *this;
+ }
+ CombinedIterator operator-(difference_type n) const {
+ CombinedIterator res = *this;
+ return res -= n;
+ }
+ difference_type operator-(const CombinedIterator& other) {
+ return it1 - other.it1;
+ }
+
+ It1 it1;
+ It2 it2;
+};
+
+} // namespace android
diff --git a/libs/androidfw/tests/AndroidTest_Benchmarks.xml b/libs/androidfw/tests/AndroidTest_Benchmarks.xml
new file mode 100644
index 0000000..e61e46f
--- /dev/null
+++ b/libs/androidfw/tests/AndroidTest_Benchmarks.xml
@@ -0,0 +1,32 @@
+<?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 libandroidfw_benchmarks and libandroidfw_tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native-metric" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libandroidfw_benchmarks->/data/local/tmp/libandroidfw_benchmarks" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
+ <option name="native-benchmark-device-path" value="/data/local/tmp" />
+ <option name="benchmark-module-name" value="libandroidfw_benchmarks" />
+ <!-- The GoogleBenchmarkTest class ordinarily expects every file in the benchmark's
+ directory (recursively) to be a google-benchmark binary, so we need this setting to
+ avoid failing on the test data files. -->
+ <option name="file-exclusion-filter-regex" value=".*\.(apk|config)$" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 2caa98c..136f5ea 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -37,7 +37,7 @@
static void BM_AssetManagerLoadAssets(benchmark::State& state) {
std::string path = GetTestDataPath() + "/basic/basic.apk";
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
auto apk = ApkAssets::Load(path);
AssetManager2 assets;
assets.SetApkAssets({apk});
@@ -47,7 +47,7 @@
static void BM_AssetManagerLoadAssetsOld(benchmark::State& state) {
String8 path((GetTestDataPath() + "/basic/basic.apk").data());
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
AssetManager assets;
assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
false /* isSystemAsset */);
@@ -60,7 +60,7 @@
static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) {
std::string path = kFrameworkPath;
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
auto apk = ApkAssets::Load(path);
AssetManager2 assets;
assets.SetApkAssets({apk});
@@ -70,7 +70,7 @@
static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) {
String8 path(kFrameworkPath);
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
AssetManager assets;
assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
false /* isSystemAsset */);
@@ -138,7 +138,7 @@
AssetManager2 assets;
assets.SetApkAssets({apk});
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
auto bag = assets.GetBag(app::R::style::StyleTwo);
if (!bag.has_value()) {
state.SkipWithError("Failed to load get bag");
@@ -165,7 +165,7 @@
const ResTable& table = assets.getResources(true);
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
const ResTable::bag_entry* bag_begin;
const ssize_t N = table.lockBag(app::R::style::StyleTwo, &bag_begin);
const ResTable::bag_entry* const bag_end = bag_begin + N;
@@ -190,7 +190,7 @@
AssetManager2 assets;
assets.SetApkAssets({apk});
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
std::set<std::string> locales =
assets.GetResourceLocales(false /*exclude_system*/, true /*merge_equivalent_languages*/);
benchmark::DoNotOptimize(locales);
@@ -208,7 +208,7 @@
const ResTable& table = assets.getResources(true);
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
Vector<String8> locales;
table.getLocales(&locales, true /*includeSystemLocales*/, true /*mergeEquivalentLangs*/);
benchmark::DoNotOptimize(locales);
@@ -231,7 +231,7 @@
std::vector<ResTable_config> configs;
configs.push_back(config);
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
configs[0].sdkVersion = ~configs[0].sdkVersion;
assets.SetConfigurations(configs);
}
@@ -251,7 +251,7 @@
ResTable_config config;
memset(&config, 0, sizeof(config));
- while (state.KeepRunning()) {
+ for (auto&& _ : state) {
config.sdkVersion = ~config.sdkVersion;
assets.setConfiguration(config);
}
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index 8b883f4..e3fc0a0 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -28,7 +28,7 @@
for (const std::string& path : paths) {
if (!assetmanager.addAssetPath(String8(path.c_str()), nullptr /* cookie */,
false /* appAsLib */, false /* isSystemAssets */)) {
- state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
+ state.SkipWithError(base::StringPrintf("Failed to old-load assets %s", path.c_str()).c_str());
return;
}
}
@@ -57,7 +57,7 @@
for (const std::string& path : paths) {
auto apk = ApkAssets::Load(path);
if (apk == nullptr) {
- state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
+ state.SkipWithError(base::StringPrintf("Failed to new-load assets %s", path.c_str()).c_str());
return;
}
apk_assets.push_back(std::move(apk));
diff --git a/libs/androidfw/tests/CombinedIterator_test.cpp b/libs/androidfw/tests/CombinedIterator_test.cpp
new file mode 100644
index 0000000..c1228f3
--- /dev/null
+++ b/libs/androidfw/tests/CombinedIterator_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/CombinedIterator.h"
+
+#include <algorithm>
+#include <string>
+#include <strstream>
+#include <utility>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace android {
+
+template <class Coll>
+std::string toString(const Coll& coll) {
+ std::stringstream res;
+ res << "(" << std::size(coll) << ")";
+ if (std::size(coll)) {
+ res << "{" << coll[0];
+ for (int i = 1; i != std::size(coll); ++i) {
+ res << "," << coll[i];
+ }
+ res << "}";
+ }
+ return res.str();
+}
+
+template <class Coll>
+void AssertCollectionEq(const Coll& first, const Coll& second) {
+ ASSERT_EQ(std::size(first), std::size(second))
+ << "first: " << toString(first) << ", second: " << toString(second);
+ for (int i = 0; i != std::size(first); ++i) {
+ ASSERT_EQ(first[i], second[i])
+ << "index: " << i << " first: " << toString(first) << ", second: " << toString(second);
+ }
+}
+
+TEST(CombinedIteratorTest, Sorting) {
+ std::vector<int> v1 = {2, 1, 3, 4, 0};
+ std::vector<int> v2 = {20, 10, 30, 40, 0};
+
+ std::sort(CombinedIterator(v1.begin(), v2.begin()), CombinedIterator(v1.end(), v2.end()));
+
+ ASSERT_EQ(v1.size(), v2.size());
+ ASSERT_TRUE(std::is_sorted(v1.begin(), v1.end()));
+ ASSERT_TRUE(std::is_sorted(v2.begin(), v2.end()));
+ AssertCollectionEq(v1, {0, 1, 2, 3, 4});
+ AssertCollectionEq(v2, {0, 10, 20, 30, 40});
+}
+
+TEST(CombinedIteratorTest, Removing) {
+ std::vector<int> v1 = {1, 2, 3, 4, 5, 5, 5, 6};
+ std::vector<int> v2 = {10, 20, 30, 40, 50, 50, 50, 60};
+
+ auto newEnd =
+ std::remove_if(CombinedIterator(v1.begin(), v2.begin()), CombinedIterator(v1.end(), v2.end()),
+ [](auto&& pair) { return pair.first >= 3 && pair.first <= 5; });
+
+ ASSERT_EQ(newEnd.it1, v1.begin() + 3);
+ ASSERT_EQ(newEnd.it2, v2.begin() + 3);
+
+ v1.erase(newEnd.it1, v1.end());
+ AssertCollectionEq(v1, {1, 2, 6});
+ v2.erase(newEnd.it2, v2.end());
+ AssertCollectionEq(v2, {10, 20, 60});
+}
+
+TEST(CombinedIteratorTest, InplaceMerge) {
+ std::vector<int> v1 = {1, 3, 4, 7, 2, 5, 6};
+ std::vector<int> v2 = {10, 30, 40, 70, 20, 50, 60};
+
+ std::inplace_merge(CombinedIterator(v1.begin(), v2.begin()),
+ CombinedIterator(v1.begin() + 4, v2.begin() + 4),
+ CombinedIterator(v1.end(), v2.end()));
+ ASSERT_TRUE(std::is_sorted(v1.begin(), v1.end()));
+ ASSERT_TRUE(std::is_sorted(v2.begin(), v2.end()));
+
+ AssertCollectionEq(v1, {1, 2, 3, 4, 5, 6, 7});
+ AssertCollectionEq(v2, {10, 20, 30, 40, 50, 60, 70});
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
index dfbb5a7..bf89617 100644
--- a/libs/androidfw/tests/Theme_bench.cpp
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -27,6 +27,10 @@
constexpr const static uint32_t kStyleId = 0x01030237u; // android:style/Theme.Material.Light
constexpr const static uint32_t kAttrId = 0x01010030u; // android:attr/colorForeground
+constexpr const static uint32_t kStyle2Id = 0x01030224u; // android:style/Theme.Material
+constexpr const static uint32_t kStyle3Id = 0x0103024du; // android:style/Widget.Material
+constexpr const static uint32_t kStyle4Id = 0x0103028eu; // android:style/Widget.Material.Light
+
static void BM_ThemeApplyStyleFramework(benchmark::State& state) {
auto apk = ApkAssets::Load(kFrameworkPath);
if (apk == nullptr) {
@@ -61,6 +65,32 @@
}
BENCHMARK(BM_ThemeApplyStyleFrameworkOld);
+static void BM_ThemeRebaseFramework(benchmark::State& state) {
+ auto apk = ApkAssets::Load(kFrameworkPath);
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk});
+
+ // Create two arrays of styles to switch between back and forth.
+ const uint32_t styles1[] = {kStyle2Id, kStyleId, kStyle3Id};
+ const uint8_t force1[std::size(styles1)] = {false, true, false};
+ const uint32_t styles2[] = {kStyleId, kStyle2Id, kStyle4Id, kStyle3Id};
+ const uint8_t force2[std::size(styles2)] = {false, true, true, false};
+ const auto theme = assets.NewTheme();
+ // Initialize the theme to make the first iteration the same as the rest.
+ theme->Rebase(&assets, styles1, force1, std::size(force1));
+
+ while (state.KeepRunning()) {
+ theme->Rebase(&assets, styles2, force2, std::size(force2));
+ theme->Rebase(&assets, styles1, force1, std::size(force1));
+ }
+}
+BENCHMARK(BM_ThemeRebaseFramework);
+
static void BM_ThemeGetAttribute(benchmark::State& state) {
auto apk = ApkAssets::Load(kFrameworkPath);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 0d0af11..4d185c6 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -28,8 +28,8 @@
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
+#include <include/gpu/vk/VulkanBackendContext.h>
#include <ui/FatVector.h>
-#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
#include <sstream>
@@ -141,7 +141,8 @@
mPhysicalDeviceFeatures2 = {};
}
-void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) {
+void VulkanManager::setupDevice(skgpu::VulkanExtensions& grExtensions,
+ VkPhysicalDeviceFeatures2& features) {
VkResult err;
constexpr VkApplicationInfo app_info = {
@@ -506,7 +507,7 @@
return vkGetInstanceProcAddr(instance, proc_name);
};
- GrVkBackendContext backendContext;
+ skgpu::VulkanBackendContext backendContext;
backendContext.fInstance = mInstance;
backendContext.fPhysicalDevice = mPhysicalDevice;
backendContext.fDevice = mDevice;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index b92ebb3..08f9d42 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -24,8 +24,7 @@
#include <SkSurface.h>
#include <android-base/unique_fd.h>
#include <utils/StrongPointer.h>
-#include <vk/GrVkBackendContext.h>
-#include <vk/GrVkExtensions.h>
+#include <vk/VulkanExtensions.h>
#include <vulkan/vulkan.h>
// VK_ANDROID_frame_boundary is a bespoke extension defined by AGI
@@ -127,7 +126,7 @@
// Sets up the VkInstance and VkDevice objects. Also fills out the passed in
// VkPhysicalDeviceFeatures struct.
- void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
+ void setupDevice(skgpu::VulkanExtensions&, VkPhysicalDeviceFeatures2&);
// simple wrapper class that exists only to initialize a pointer to NULL
template <typename FNPTR_TYPE>
@@ -206,7 +205,7 @@
BufferAge,
};
SwapBehavior mSwapBehavior = SwapBehavior::Discard;
- GrVkExtensions mExtensions;
+ skgpu::VulkanExtensions mExtensions;
uint32_t mDriverVersion = 0;
std::once_flag mInitFlag;
diff --git a/libs/nativehelper_jvm/Android.bp b/libs/nativehelper_jvm/Android.bp
new file mode 100644
index 0000000..b5b7028
--- /dev/null
+++ b/libs/nativehelper_jvm/Android.bp
@@ -0,0 +1,19 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_library_host_static {
+ name: "libnativehelper_jvm",
+ srcs: [
+ "JNIPlatformHelp.c",
+ "JniConstants.c",
+ "file_descriptor_jni.c",
+ ],
+ whole_static_libs: ["libnativehelper_any_vm"],
+ export_static_lib_headers: ["libnativehelper_any_vm"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libs/nativehelper_jvm/JNIPlatformHelp.c b/libs/nativehelper_jvm/JNIPlatformHelp.c
new file mode 100644
index 0000000..9df31a8
--- /dev/null
+++ b/libs/nativehelper_jvm/JNIPlatformHelp.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/JNIPlatformHelp.h>
+
+#include <stddef.h>
+
+#include "JniConstants.h"
+
+static int GetBufferPosition(JNIEnv* env, jobject nioBuffer) {
+ return(*env)->GetIntField(env, nioBuffer, JniConstants_NioBuffer_position(env));
+}
+
+static int GetBufferLimit(JNIEnv* env, jobject nioBuffer) {
+ return(*env)->GetIntField(env, nioBuffer, JniConstants_NioBuffer_limit(env));
+}
+
+static int GetBufferElementSizeShift(JNIEnv* env, jobject nioBuffer) {
+ jclass byteBufferClass = JniConstants_NioByteBufferClass(env);
+ jclass shortBufferClass = JniConstants_NioShortBufferClass(env);
+ jclass charBufferClass = JniConstants_NioCharBufferClass(env);
+ jclass intBufferClass = JniConstants_NioIntBufferClass(env);
+ jclass floatBufferClass = JniConstants_NioFloatBufferClass(env);
+ jclass longBufferClass = JniConstants_NioLongBufferClass(env);
+ jclass doubleBufferClass = JniConstants_NioDoubleBufferClass(env);
+
+ // Check the type of the Buffer
+ if ((*env)->IsInstanceOf(env, nioBuffer, byteBufferClass)) {
+ return 0;
+ } else if ((*env)->IsInstanceOf(env, nioBuffer, shortBufferClass) ||
+ (*env)->IsInstanceOf(env, nioBuffer, charBufferClass)) {
+ return 1;
+ } else if ((*env)->IsInstanceOf(env, nioBuffer, intBufferClass) ||
+ (*env)->IsInstanceOf(env, nioBuffer, floatBufferClass)) {
+ return 2;
+ } else if ((*env)->IsInstanceOf(env, nioBuffer, longBufferClass) ||
+ (*env)->IsInstanceOf(env, nioBuffer, doubleBufferClass)) {
+ return 3;
+ }
+ return 0;
+}
+
+jarray jniGetNioBufferBaseArray(JNIEnv* env, jobject nioBuffer) {
+ jmethodID hasArrayMethod = JniConstants_NioBuffer_hasArray(env);
+ jboolean hasArray = (*env)->CallBooleanMethod(env, nioBuffer, hasArrayMethod);
+ if (hasArray) {
+ jmethodID arrayMethod = JniConstants_NioBuffer_array(env);
+ return (*env)->CallObjectMethod(env, nioBuffer, arrayMethod);
+ } else {
+ return NULL;
+ }
+}
+
+int jniGetNioBufferBaseArrayOffset(JNIEnv* env, jobject nioBuffer) {
+ jmethodID hasArrayMethod = JniConstants_NioBuffer_hasArray(env);
+ jboolean hasArray = (*env)->CallBooleanMethod(env, nioBuffer, hasArrayMethod);
+ if (hasArray) {
+ jmethodID arrayOffsetMethod = JniConstants_NioBuffer_arrayOffset(env);
+ jint arrayOffset = (*env)->CallIntMethod(env, nioBuffer, arrayOffsetMethod);
+ const int position = GetBufferPosition(env, nioBuffer);
+ jint elementSizeShift = GetBufferElementSizeShift(env, nioBuffer);
+ return (arrayOffset + position) << elementSizeShift;
+ } else {
+ return 0;
+ }
+}
+
+jlong jniGetNioBufferPointer(JNIEnv* env, jobject nioBuffer) {
+ // in Java 11, the address field of a HeapByteBuffer contains a non-zero value despite
+ // HeapByteBuffer being a non-direct buffer. In that case, this should still return 0.
+ jmethodID isDirectMethod = JniConstants_NioBuffer_isDirect(env);
+ jboolean isDirect = (*env)->CallBooleanMethod(env, nioBuffer, isDirectMethod);
+ if (isDirect == JNI_FALSE) {
+ return 0L;
+ }
+ jlong baseAddress = (*env)->GetLongField(env, nioBuffer, JniConstants_NioBuffer_address(env));
+ if (baseAddress != 0) {
+ const int position = GetBufferPosition(env, nioBuffer);
+ const int shift = GetBufferElementSizeShift(env, nioBuffer);
+ baseAddress += position << shift;
+ }
+ return baseAddress;
+}
+
+jlong jniGetNioBufferFields(JNIEnv* env, jobject nioBuffer,
+ jint* position, jint* limit, jint* elementSizeShift) {
+ *position = GetBufferPosition(env, nioBuffer);
+ *limit = GetBufferLimit(env, nioBuffer);
+ *elementSizeShift = GetBufferElementSizeShift(env, nioBuffer);
+ return (*env)->GetLongField(env, nioBuffer, JniConstants_NioBuffer_address(env));
+}
diff --git a/libs/nativehelper_jvm/JniConstants.c b/libs/nativehelper_jvm/JniConstants.c
new file mode 100644
index 0000000..ca58f61
--- /dev/null
+++ b/libs/nativehelper_jvm/JniConstants.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "JniConstants.h"
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#define LOG_TAG "JniConstants"
+#include <log/log.h>
+
+// jclass constants list:
+// <class, signature, androidOnly>
+#define JCLASS_CONSTANTS_LIST(V) \
+ V(FileDescriptor, "java/io/FileDescriptor", false) \
+ V(NioBuffer, "java/nio/Buffer", false) \
+ V(NioByteBuffer, "java/nio/ByteBuffer", false) \
+ V(NioShortBuffer, "java/nio/ShortBuffer", false) \
+ V(NioCharBuffer, "java/nio/CharBuffer", false) \
+ V(NioIntBuffer, "java/nio/IntBuffer", false) \
+ V(NioFloatBuffer, "java/nio/FloatBuffer", false) \
+ V(NioLongBuffer, "java/nio/LongBuffer", false) \
+ V(NioDoubleBuffer, "java/nio/DoubleBuffer", false)
+
+// jmethodID's of public methods constants list:
+// <Class, method, method-string, signature, is_static>
+#define JMETHODID_CONSTANTS_LIST(V) \
+ V(FileDescriptor, init, "<init>", "()V", false) \
+ V(NioBuffer, array, "array", "()Ljava/lang/Object;", false) \
+ V(NioBuffer, hasArray, "hasArray", "()Z", false) \
+ V(NioBuffer, isDirect, "isDirect", "()Z", false) \
+ V(NioBuffer, arrayOffset, "arrayOffset", "()I", false)
+
+// jfieldID constants list:
+// <Class, field, signature, is_static>
+#define JFIELDID_CONSTANTS_LIST(V) \
+ V(FileDescriptor, fd, "I", false) \
+ V(NioBuffer, address, "J", false) \
+ V(NioBuffer, limit, "I", false) \
+ V(NioBuffer, position, "I", false)
+
+#define CLASS_NAME(cls) g_ ## cls
+#define METHOD_NAME(cls, method) g_ ## cls ## _ ## method
+#define FIELD_NAME(cls, field) g_ ## cls ## _ ## field
+
+//
+// Declare storage for cached classes, methods and fields.
+//
+
+#define JCLASS_DECLARE_STORAGE(cls, ...) \
+ static jclass CLASS_NAME(cls) = NULL;
+JCLASS_CONSTANTS_LIST(JCLASS_DECLARE_STORAGE)
+#undef JCLASS_DECLARE_STORAGE
+
+#define JMETHODID_DECLARE_STORAGE(cls, method, ...) \
+ static jmethodID METHOD_NAME(cls, method) = NULL;
+JMETHODID_CONSTANTS_LIST(JMETHODID_DECLARE_STORAGE)
+#undef JMETHODID_DECLARE_STORAGE
+
+#define JFIELDID_DECLARE_STORAGE(cls, field, ...) \
+ static jfieldID FIELD_NAME(cls, field) = NULL;
+JFIELDID_CONSTANTS_LIST(JFIELDID_DECLARE_STORAGE)
+#undef JFIELDID_DECLARE_STORAGE
+
+//
+// Helper methods
+//
+
+static jclass FindClass(JNIEnv* env, const char* signature, bool androidOnly) {
+ jclass cls = (*env)->FindClass(env, signature);
+ if (cls == NULL) {
+ LOG_ALWAYS_FATAL_IF(!androidOnly, "Class not found: %s", signature);
+ return NULL;
+ }
+ return (*env)->NewGlobalRef(env, cls);
+}
+
+static jmethodID FindMethod(JNIEnv* env, jclass cls,
+ const char* name, const char* signature, bool isStatic) {
+ jmethodID method;
+ if (isStatic) {
+ method = (*env)->GetStaticMethodID(env, cls, name, signature);
+ } else {
+ method = (*env)->GetMethodID(env, cls, name, signature);
+ }
+ LOG_ALWAYS_FATAL_IF(method == NULL, "Method not found: %s:%s", name, signature);
+ return method;
+}
+
+static jfieldID FindField(JNIEnv* env, jclass cls,
+ const char* name, const char* signature, bool isStatic) {
+ jfieldID field;
+ if (isStatic) {
+ field = (*env)->GetStaticFieldID(env, cls, name, signature);
+ } else {
+ field = (*env)->GetFieldID(env, cls, name, signature);
+ }
+ LOG_ALWAYS_FATAL_IF(field == NULL, "Field not found: %s:%s", name, signature);
+ return field;
+}
+
+static pthread_once_t g_initialized = PTHREAD_ONCE_INIT;
+static JNIEnv* g_init_env;
+
+static void InitializeConstants() {
+ // Initialize cached classes.
+#define JCLASS_INITIALIZE(cls, signature, androidOnly) \
+ CLASS_NAME(cls) = FindClass(g_init_env, signature, androidOnly);
+ JCLASS_CONSTANTS_LIST(JCLASS_INITIALIZE)
+#undef JCLASS_INITIALIZE
+
+ // Initialize cached methods.
+#define JMETHODID_INITIALIZE(cls, method, name, signature, isStatic) \
+ METHOD_NAME(cls, method) = \
+ FindMethod(g_init_env, CLASS_NAME(cls), name, signature, isStatic);
+ JMETHODID_CONSTANTS_LIST(JMETHODID_INITIALIZE)
+#undef JMETHODID_INITIALIZE
+
+ // Initialize cached fields.
+#define JFIELDID_INITIALIZE(cls, field, signature, isStatic) \
+ FIELD_NAME(cls, field) = \
+ FindField(g_init_env, CLASS_NAME(cls), #field, signature, isStatic);
+ JFIELDID_CONSTANTS_LIST(JFIELDID_INITIALIZE)
+#undef JFIELDID_INITIALIZE
+}
+
+void EnsureInitialized(JNIEnv* env) {
+ // This method has to be called in every cache accesses because library can be built
+ // 2 different ways and existing usage for compat version doesn't have a good hook for
+ // initialization and is widely used.
+ g_init_env = env;
+ pthread_once(&g_initialized, InitializeConstants);
+}
+
+// API exported by libnativehelper_api.h.
+
+void jniUninitializeConstants() {
+ // Uninitialize cached classes, methods and fields.
+ //
+ // NB we assume the runtime is stopped at this point and do not delete global
+ // references.
+#define JCLASS_INVALIDATE(cls, ...) CLASS_NAME(cls) = NULL;
+ JCLASS_CONSTANTS_LIST(JCLASS_INVALIDATE);
+#undef JCLASS_INVALIDATE
+
+#define JMETHODID_INVALIDATE(cls, method, ...) METHOD_NAME(cls, method) = NULL;
+ JMETHODID_CONSTANTS_LIST(JMETHODID_INVALIDATE);
+#undef JMETHODID_INVALIDATE
+
+#define JFIELDID_INVALIDATE(cls, field, ...) FIELD_NAME(cls, field) = NULL;
+ JFIELDID_CONSTANTS_LIST(JFIELDID_INVALIDATE);
+#undef JFIELDID_INVALIDATE
+
+ // If jniConstantsUninitialize is called, runtime has shutdown. Reset
+ // state as some tests re-start the runtime.
+ pthread_once_t o = PTHREAD_ONCE_INIT;
+ memcpy(&g_initialized, &o, sizeof(o));
+}
+
+//
+// Accessors
+//
+
+#define JCLASS_ACCESSOR_IMPL(cls, ...) \
+jclass JniConstants_ ## cls ## Class(JNIEnv* env) { \
+ EnsureInitialized(env); \
+ return CLASS_NAME(cls); \
+}
+JCLASS_CONSTANTS_LIST(JCLASS_ACCESSOR_IMPL)
+#undef JCLASS_ACCESSOR_IMPL
+
+#define JMETHODID_ACCESSOR_IMPL(cls, method, ...) \
+jmethodID JniConstants_ ## cls ## _ ## method(JNIEnv* env) { \
+ EnsureInitialized(env); \
+ return METHOD_NAME(cls, method); \
+}
+JMETHODID_CONSTANTS_LIST(JMETHODID_ACCESSOR_IMPL)
+
+#define JFIELDID_ACCESSOR_IMPL(cls, field, ...) \
+jfieldID JniConstants_ ## cls ## _ ## field(JNIEnv* env) { \
+ EnsureInitialized(env); \
+ return FIELD_NAME(cls, field); \
+}
+JFIELDID_CONSTANTS_LIST(JFIELDID_ACCESSOR_IMPL)
diff --git a/libs/nativehelper_jvm/JniConstants.h b/libs/nativehelper_jvm/JniConstants.h
new file mode 100644
index 0000000..e7a266d
--- /dev/null
+++ b/libs/nativehelper_jvm/JniConstants.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+
+#include <jni.h>
+
+__BEGIN_DECLS
+
+//
+// Classes in constants cache.
+//
+// NB The implementations of these methods are generated by the JCLASS_ACCESSOR_IMPL macro in
+// JniConstants.c.
+//
+jclass JniConstants_FileDescriptorClass(JNIEnv* env);
+jclass JniConstants_NioByteBufferClass(JNIEnv* env);
+jclass JniConstants_NioShortBufferClass(JNIEnv* env);
+jclass JniConstants_NioCharBufferClass(JNIEnv* env);
+jclass JniConstants_NioIntBufferClass(JNIEnv* env);
+jclass JniConstants_NioFloatBufferClass(JNIEnv* env);
+jclass JniConstants_NioLongBufferClass(JNIEnv* env);
+jclass JniConstants_NioDoubleBufferClass(JNIEnv* env);
+
+//
+// Methods in the constants cache.
+//
+// NB The implementations of these methods are generated by the JMETHODID_ACCESSOR_IMPL macro in
+// JniConstants.c.
+//
+jmethodID JniConstants_FileDescriptor_init(JNIEnv* env);
+jmethodID JniConstants_NioBuffer_array(JNIEnv* env);
+jmethodID JniConstants_NioBuffer_arrayOffset(JNIEnv* env);
+jmethodID JniConstants_NioBuffer_hasArray(JNIEnv* env);
+jmethodID JniConstants_NioBuffer_isDirect(JNIEnv* env);
+
+//
+// Fields in the constants cache.
+//
+// NB The implementations of these methods are generated by the JFIELDID_ACCESSOR_IMPL macro in
+// JniConstants.c.
+//
+jfieldID JniConstants_FileDescriptor_fd(JNIEnv* env);
+jfieldID JniConstants_NioBuffer_address(JNIEnv* env);
+jfieldID JniConstants_NioBuffer_limit(JNIEnv* env);
+jfieldID JniConstants_NioBuffer_position(JNIEnv* env);
+
+__END_DECLS
diff --git a/libs/nativehelper_jvm/OWNERS b/libs/nativehelper_jvm/OWNERS
new file mode 100644
index 0000000..5d55f6e
--- /dev/null
+++ b/libs/nativehelper_jvm/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 326772
+
+include /libs/hwui/OWNERS
+include platform/libnativehelper:/OWNERS
+
+diegoperez@google.com
+jgaillard@google.com
diff --git a/libs/nativehelper_jvm/README b/libs/nativehelper_jvm/README
new file mode 100644
index 0000000..755c422
--- /dev/null
+++ b/libs/nativehelper_jvm/README
@@ -0,0 +1,2 @@
+libnativehelper_jvm is a JVM-compatible version of libnativehelper.
+It should be used instead of libnativehelper whenever a host library is meant to run on a JVM.
\ No newline at end of file
diff --git a/libs/nativehelper_jvm/file_descriptor_jni.c b/libs/nativehelper_jvm/file_descriptor_jni.c
new file mode 100644
index 0000000..36880cd
--- /dev/null
+++ b/libs/nativehelper_jvm/file_descriptor_jni.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/file_descriptor_jni.h>
+
+#include <stddef.h>
+
+#define LOG_TAG "file_descriptor_jni"
+#include <log/log.h>
+
+#include "JniConstants.h"
+
+static void EnsureArgumentIsFileDescriptor(JNIEnv* env, jobject instance) {
+ LOG_ALWAYS_FATAL_IF(instance == NULL, "FileDescriptor is NULL");
+ jclass jifd = JniConstants_FileDescriptorClass(env);
+ LOG_ALWAYS_FATAL_IF(!(*env)->IsInstanceOf(env, instance, jifd),
+ "Argument is not a FileDescriptor");
+}
+
+JNIEXPORT _Nullable jobject AFileDescriptor_create(JNIEnv* env) {
+ return (*env)->NewObject(env,
+ JniConstants_FileDescriptorClass(env),
+ JniConstants_FileDescriptor_init(env));
+}
+
+JNIEXPORT int AFileDescriptor_getFd(JNIEnv* env, jobject fileDescriptor) {
+ EnsureArgumentIsFileDescriptor(env, fileDescriptor);
+ return (*env)->GetIntField(env, fileDescriptor, JniConstants_FileDescriptor_fd(env));
+}
+
+JNIEXPORT void AFileDescriptor_setFd(JNIEnv* env, jobject fileDescriptor, int fd) {
+ EnsureArgumentIsFileDescriptor(env, fileDescriptor);
+ (*env)->SetIntField(env, fileDescriptor, JniConstants_FileDescriptor_fd(env), fd);
+}
diff --git a/lint-baseline.xml b/lint-baseline.xml
index 660884a..0320aab 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -562,4 +562,10707 @@
column="74"/>
</issue>
+ <issue
+ id="FlaggedApi"
+ message="Method `getItemCount()` is a flagged API and should be inside an `if (Flags.collectionInfoItemCounts())` check (or annotate the surrounding method `writeToParcelNoRecycle` with `@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) to transfer requirement to caller`)"
+ errorLine1=" parcel.writeInt(mCollectionInfo.getItemCount());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java"
+ line="4498"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getImportantForAccessibilityItemCount()` is a flagged API and should be inside an `if (Flags.collectionInfoItemCounts())` check (or annotate the surrounding method `writeToParcelNoRecycle` with `@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) to transfer requirement to caller`)"
+ errorLine1=" parcel.writeInt(mCollectionInfo.getImportantForAccessibilityItemCount());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java"
+ line="4499"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isGranularScrollingSupported()` is a flagged API and should be inside an `if (Flags.granularScrolling())` check (or annotate the surrounding method `toString` with `@FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING) to transfer requirement to caller`)"
+ errorLine1=" builder.append("; granularScrollingSupported: ").append(isGranularScrollingSupported());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java"
+ line="5079"
+ column="65"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `UNDEFINED` is a flagged API and should be inside an `if (Flags.collectionInfoItemCounts())` check (or annotate the surrounding method `CollectionInfo` with `@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) to transfer requirement to caller`)"
+ errorLine1=" mItemCount = UNDEFINED;"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java"
+ line="6211"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `UNDEFINED` is a flagged API and should be inside an `if (Flags.collectionInfoItemCounts())` check (or annotate the surrounding method `CollectionInfo` with `@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) to transfer requirement to caller`)"
+ errorLine1=" mImportantForAccessibilityItemCount = UNDEFINED;"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java"
+ line="6212"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `UNDEFINED` is a flagged API and should be inside an `if (Flags.collectionInfoItemCounts())` check (or annotate the surrounding method `clear` with `@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) to transfer requirement to caller`)"
+ errorLine1=" mItemCount = UNDEFINED;"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java"
+ line="6319"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `UNDEFINED` is a flagged API and should be inside an `if (Flags.collectionInfoItemCounts())` check (or annotate the surrounding method `clear` with `@FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) to transfer requirement to caller`)"
+ errorLine1=" mImportantForAccessibilityItemCount = UNDEFINED;"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java"
+ line="6320"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="800"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="811"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="811"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="811"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="819"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="819"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="819"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="819"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="819"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="819"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="819"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="827"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java"
+ line="827"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setObservedMotionEventSources()` is a flagged API and should be inside an `if (Flags.motionEventObserving())` check (or annotate the surrounding method `initFromParcel` with `@FlaggedApi(Flags.FLAG_MOTION_EVENT_OBSERVING) to transfer requirement to caller`)"
+ errorLine1=" setObservedMotionEventSources(parcel.readInt());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/accessibilityservice/AccessibilityServiceInfo.java"
+ line="1420"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_WINDOW_CONTROL` is a flagged API and should be inside an `if (Flags.addTypeWindowControl())` check (or annotate the surrounding method `typeToString` with `@FlaggedApi(Flags.FLAG_ADD_TYPE_WINDOW_CONTROL) to transfer requirement to caller`)"
+ errorLine1=" if (Flags.addTypeWindowControl() && type == TYPE_WINDOW_CONTROL) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/accessibility/AccessibilityWindowInfo.java"
+ line="883"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `requestPermissions()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `requestPermissions` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" requestPermissions(permissions, requestCode, getDeviceId());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/Activity.java"
+ line="5636"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `EXTRA_REQUEST_PERMISSIONS_DEVICE_ID` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `dispatchRequestPermissionsResult` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, Context.DEVICE_ID_DEFAULT"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/Activity.java"
+ line="9530"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onRequestPermissionsResult()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `dispatchRequestPermissionsResult` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" onRequestPermissionsResult(requestCode, permissions, grantResults, deviceId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/Activity.java"
+ line="9532"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `uptimeNanos()` is a flagged API and should be inside an `if (Flags.adpfGpuReportActualWorkDuration())` check (or annotate the surrounding method `handleBindApplication` with `@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) to transfer requirement to caller`)"
+ errorLine1=" timestampApplicationOnCreateNs = SystemClock.uptimeNanos();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ActivityThread.java"
+ line="7503"
+ column="50"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `SeServiceManager()` is a flagged API and should be inside an `if (Flags.enableNfcMainline())` check (or annotate the surrounding method `initializeMainlineModules` with `@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) to transfer requirement to caller`)"
+ errorLine1=" SeFrameworkInitializer.setSeServiceManager(new SeServiceManager());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ActivityThread.java"
+ line="8725"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setSeServiceManager()` is a flagged API and should be inside an `if (Flags.enableNfcMainline())` check (or annotate the surrounding method `initializeMainlineModules` with `@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) to transfer requirement to caller`)"
+ errorLine1=" SeFrameworkInitializer.setSeServiceManager(new SeServiceManager());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ActivityThread.java"
+ line="8725"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `ProfilingServiceManager()` is a flagged API and should be inside an `if (Flags.telemetryApisFrameworkInitialization())` check (or annotate the surrounding method `initializeMainlineModules` with `@FlaggedApi(Flags.FLAG_TELEMETRY_APIS_FRAMEWORK_INITIALIZATION) to transfer requirement to caller`)"
+ errorLine1=" ProfilingFrameworkInitializer.setProfilingServiceManager(new ProfilingServiceManager());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ActivityThread.java"
+ line="8727"
+ column="70"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `TvExtender()` is a flagged API and should be inside an `if (Flags.apiTvextender())` check (or annotate the surrounding method `createNotification` with `@FlaggedApi(Flags.FLAG_API_TVEXTENDER) to transfer requirement to caller`)"
+ errorLine1=" .extend(new Notification.TvExtender()"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/debug/AdbNotifications.java"
+ line="95"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setChannelId()` is a flagged API and should be inside an `if (Flags.apiTvextender())` check (or annotate the surrounding method `createNotification` with `@FlaggedApi(Flags.FLAG_API_TVEXTENDER) to transfer requirement to caller`)"
+ errorLine1=" .extend(new Notification.TvExtender()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/debug/AdbNotifications.java"
+ line="95"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isMgf1DigestsSpecified()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `initialize` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" if (spec.isMgf1DigestsSpecified()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java"
+ line="346"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getMgf1Digests()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `initialize` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" Set<String> mgfDigests = spec.getMgf1Digests();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java"
+ line="349"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isMgf1DigestsSpecified()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `setPrivateKeyEntry` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" if (spec.isMgf1DigestsSpecified()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java"
+ line="539"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getMgf1Digests()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `setPrivateKeyEntry` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" for (String mgf1Digest : spec.getMgf1Digests()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java"
+ line="540"
+ column="50"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_RCS_STRING` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" APN_TYPE_STRING_MAP.put(TYPE_RCS_STRING, TYPE_RCS);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="491"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_RCS` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" APN_TYPE_STRING_MAP.put(TYPE_RCS_STRING, TYPE_RCS);"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="491"
+ column="50"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_RCS_STRING` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" APN_TYPE_INT_MAP.put(TYPE_RCS, TYPE_RCS_STRING);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="509"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_RCS` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" APN_TYPE_INT_MAP.put(TYPE_RCS, TYPE_RCS_STRING);"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="509"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `MTU_V4` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `makeApnSetting` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" int mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V4));"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1075"
+ column="83"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setAlwaysOn()` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `makeApnSetting` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" return new Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1080"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `MTU_V6` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `makeApnSetting` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" .setMtuV6(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V6)))"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1126"
+ column="89"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ALWAYS_ON` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `makeApnSetting` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" .setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1)"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1137"
+ column="82"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setAlwaysOn()` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `makeApnSetting` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" return new Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1151"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `MTU_V4` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `toContentValues` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" apnValue.put(Telephony.Carriers.MTU_V4, mMtuV4);"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1500"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `MTU_V6` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `toContentValues` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" apnValue.put(Telephony.Carriers.MTU_V6, mMtuV6);"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1501"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ALWAYS_ON` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `toContentValues` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn);"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1504"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setAlwaysOn()` is a flagged API and should be inside an `if (Flags.apnSettingFieldSupportFlag())` check (or annotate the surrounding method `readFromParcel` with `@FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG) to transfer requirement to caller`)"
+ errorLine1=" return new Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="1785"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_RCS` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `build` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE | TYPE_RCS)) == 0"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/ApnSetting.java"
+ line="2386"
+ column="76"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_ACCESS_RESTRICTED_SETTINGS` is a flagged API and should be inside an `if (Flags.enhancedConfirmationModeApisEnabled())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" new AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="2983"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_CREATE_ACCESSIBILITY_OVERLAY` is a flagged API and should be inside an `if (Flags.createAccessibilityOverlayAppOpEnabled())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" OPSTR_CREATE_ACCESSIBILITY_OVERLAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="3050"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_MEDIA_ROUTING_CONTROL` is a flagged API and should be inside an `if (Flags.enablePrivilegedRoutingForMediaRoutingControl())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL) to transfer requirement to caller`)"
+ errorLine1=" new AppOpInfo.Builder(OP_MEDIA_ROUTING_CONTROL, OPSTR_MEDIA_ROUTING_CONTROL,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="3053"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_ENABLE_MOBILE_DATA_BY_USER` is a flagged API and should be inside an `if (Flags.opEnableMobileDataByUser())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER) to transfer requirement to caller`)"
+ errorLine1=" new AppOpInfo.Builder(OP_ENABLE_MOBILE_DATA_BY_USER, OPSTR_ENABLE_MOBILE_DATA_BY_USER,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="3056"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER` is a flagged API and should be inside an `if (Flags.rapidClearNotificationsByListenerAppOpEnabled())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="3061"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_EMERGENCY_LOCATION` is a flagged API and should be inside an `if (Flags.locationBypass())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_LOCATION_BYPASS) to transfer requirement to caller`)"
+ errorLine1=" new AppOpInfo.Builder(OP_EMERGENCY_LOCATION, OPSTR_EMERGENCY_LOCATION, "EMERGENCY_LOCATION")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="3077"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS` is a flagged API and should be inside an `if (Flags.redactSensitiveNotificationsFromUntrustedListeners())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS) to transfer requirement to caller`)"
+ errorLine1=" OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS, "RECEIVE_SENSITIVE_NOTIFICATIONS")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="3083"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `OpEventProxyInfo` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="3550"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `onOpChanged` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="7483"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `getPackagesForOps` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="7891"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `unsafeCheckOpRawNoThrow` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" attributionSource.getPackageName(), attributionSource.getDeviceId());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="8848"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `noteOpNoThrow` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" attributionSource.getAttributionTag(), attributionSource.getDeviceId(), message);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="9034"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `checkOpNoThrow` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" attributionSource.getDeviceId());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="9329"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `startOpNoThrow` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" attributionSource.getAttributionTag(), attributionSource.getDeviceId(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="9589"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `finishOp` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" attributionSource.getDeviceId());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AppOpsManager.java"
+ line="9842"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `EXTRA_REQUEST_PERMISSIONS_DEVICE_ID` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `buildRequestPermissionsIntent` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" intent.putExtra(EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, mContext.getDeviceId());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ApplicationPackageManager.java"
+ line="965"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `APP_METADATA_SOURCE_UNKNOWN` is a flagged API and should be inside an `if (Flags.aslInApkAppMetadataSource())` check (or annotate the surrounding method `getAppMetadataSource` with `@FlaggedApi(Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) to transfer requirement to caller`)"
+ errorLine1=" int source = PackageManager.APP_METADATA_SOURCE_UNKNOWN;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ApplicationPackageManager.java"
+ line="1283"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `isArchived` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `loadUnbadgedItemIcon` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" if (itemInfo.isArchived) {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ApplicationPackageManager.java"
+ line="3430"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `ArchivedPackageInfo()` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `getArchivedPackage` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" return new ArchivedPackageInfo(parcel);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ApplicationPackageManager.java"
+ line="3995"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPendingCredentialRequest()` is a flagged API and should be inside an `if (Flags.autofillCredmanDevIntegration())` check (or annotate the surrounding method `dump` with `@FlaggedApi(Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) to transfer requirement to caller`)"
+ errorLine1=" GetCredentialRequest getCredentialRequest = node.getPendingCredentialRequest();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/assist/AssistStructure.java"
+ line="2657"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `AttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken, null,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="112"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `AttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(uid, Process.INVALID_PID, packageName, attributionTag, token,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="126"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `AttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(uid, pid, packageName, attributionTag, token, /*renouncedPermissions*/ null,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="133"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `AttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="142"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `AttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(current.getUid(), current.getPid(), current.getPackageName(),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="150"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `AttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" current.mAttributionSourceState.renouncedPermissions, current.getDeviceId(), next);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="152"
+ column="71"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `AttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, deviceId,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="159"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withNextAttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="212"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withNextAttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), next);"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="213"
+ column="75"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withPackageName` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new AttributionSource(getUid(), getPid(), packageName, getAttributionTag(),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="218"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withPackageName` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="219"
+ column="74"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withToken` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="224"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withToken` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" token, mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="225"
+ column="70"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withPid` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="235"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withPid` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="236"
+ column="75"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `AttributionSource()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `withDeviceId` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="241"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setDeviceId()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `myAttributionSource` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new AttributionSource.Builder(uid)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="284"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isRegisteredAttributionSource()` is a flagged API and should be inside an `if (Flags.shouldRegisterAttributionSource())` check (or annotate the surrounding method `isTrusted` with `@FlaggedApi(Flags.FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE) to transfer requirement to caller`)"
+ errorLine1=" && context.getSystemService(PermissionManager.class)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/AttributionSource.java"
+ line="487"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isResumed()` is a flagged API and should be inside an `if (Flags.enableNfcMainline())` check (or annotate the surrounding method `isDisablingEnterExitEventForAutofill` with `@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) to transfer requirement to caller`)"
+ errorLine1=" return mAutoFillIgnoreFirstResumePause || !mActivity.isResumed();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/autofill/AutofillClientController.java"
+ line="473"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_NAME` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_NAME) != 0) {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AutomaticZenRule.java"
+ line="582"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_INTERRUPTION_FILTER` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_INTERRUPTION_FILTER) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AutomaticZenRule.java"
+ line="585"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_ICON` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_ICON) != 0) {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/AutomaticZenRule.java"
+ line="588"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFrameTimeNanos()` is a flagged API and should be inside an `if (Flags.expectedPresentationTimeApi())` check (or annotate the surrounding method `run` with `@FlaggedApi(Flags.FLAG_EXPECTED_PRESENTATION_TIME_API) to transfer requirement to caller`)"
+ errorLine1=" doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/BatchedInputEventReceiver.java"
+ line="130"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `Gainmap()` is a flagged API and should be inside an `if (Flags.gainmapConstructorWithMetadata())` check (or annotate the surrounding method `createBitmap` with `@FlaggedApi(Flags.FLAG_GAINMAP_CONSTRUCTOR_WITH_METADATA) to transfer requirement to caller`)"
+ errorLine1=" bitmap.setGainmap(new Gainmap(source.getGainmap(), newMapContents));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Bitmap.java"
+ line="1030"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `Gainmap()` is a flagged API and should be inside an `if (Flags.gainmapConstructorWithMetadata())` check (or annotate the surrounding method `setOverrideGainmap` with `@FlaggedApi(Flags.FLAG_GAINMAP_CONSTRUCTOR_WITH_METADATA) to transfer requirement to caller`)"
+ errorLine1=" mOverrideGainmap = new Gainmap(overrideGainmap, overrideGainmap.getGainmapContents());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/BitmapShader.java"
+ line="193"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `set()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `isBoring` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" fm.set(minimumFontMetrics);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/BoringLayout.java"
+ line="594"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `getLineMax` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" if (getUseBoundsForWidth()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/BoringLayout.java"
+ line="658"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `getLineWidth` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" if (getUseBoundsForWidth()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/BoringLayout.java"
+ line="667"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getShiftDrawingOffsetForStartOverhang()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `draw` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" if (getUseBoundsForWidth() && getShiftDrawingOffsetForStartOverhang()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/BoringLayout.java"
+ line="720"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `draw` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" if (getUseBoundsForWidth() && getShiftDrawingOffsetForStartOverhang()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/BoringLayout.java"
+ line="720"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `createFromParcelBody()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" return SignalingDataRequest.createFromParcelBody(source);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/BroadcastInfoRequest.java"
+ line="89"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `createFromParcelBody()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" return SignalingDataResponse.createFromParcelBody(source);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/BroadcastInfoResponse.java"
+ line="75"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `BUGREPORT_MODE_ONBOARDING` is a flagged API and should be inside an `if (Flags.onboardingBugreportV2Enabled())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" public static final int BUGREPORT_MODE_MAX_VALUE = BUGREPORT_MODE_ONBOARDING;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/BugreportParams.java"
+ line="139"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PROPERTY_IS_TRANSACTIONAL` is a flagged API and should be inside an `if (Flags.voipAppActionsSupport())` check (or annotate the surrounding method `propertiesToString` with `@FlaggedApi(Flags.FLAG_VOIP_APP_ACTIONS_SUPPORT) to transfer requirement to caller`)"
+ errorLine1=" if (hasProperty(properties, PROPERTY_IS_TRANSACTIONAL)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/Call.java"
+ line="854"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getImsReasonInfo()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `handleCallDisconnected` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" if (disconnectCause.getImsReasonInfo() != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/CallDiagnosticService.java"
+ line="365"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getImsReasonInfo()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `handleCallDisconnected` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" message = callDiagnostics.onCallDisconnected(disconnectCause.getImsReasonInfo());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/CallDiagnosticService.java"
+ line="366"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getTelephonyDisconnectCause()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `handleCallDisconnected` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" disconnectCause.getTelephonyDisconnectCause(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/CallDiagnosticService.java"
+ line="369"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getTelephonyPreciseDisconnectCause()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `handleCallDisconnected` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" disconnectCause.getTelephonyPreciseDisconnectCause());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/CallDiagnosticService.java"
+ line="370"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `POLICY_TYPE_CAMERA` is a flagged API and should be inside an `if (Flags.virtualCamera())` check (or annotate the surrounding method `getDevicePolicyFromContext` with `@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA) to transfer requirement to caller`)"
+ errorLine1=" return virtualDeviceManager.getDevicePolicy(context.getDeviceId(), POLICY_TYPE_CAMERA);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/Camera.java"
+ line="350"
+ column="76"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FALLBACK_PACKAGE_NAME` is a flagged API and should be inside an `if (Flags.concertMode())` check (or annotate the surrounding method `onSuccess` with `@FlaggedApi(Flags.FLAG_CONCERT_MODE) to transfer requirement to caller`)"
+ errorLine1=" .getString(FALLBACK_PACKAGE_NAME);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java"
+ line="591"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `POLICY_TYPE_CAMERA` is a flagged API and should be inside an `if (Flags.virtualCamera())` check (or annotate the surrounding method `getDevicePolicyFromContext` with `@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA) to transfer requirement to caller`)"
+ errorLine1=" return mVirtualDeviceManager.getDevicePolicy(context.getDeviceId(), POLICY_TYPE_CAMERA);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/CameraManager.java"
+ line="584"
+ column="77"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `CameraDeviceSetupImpl()` is a flagged API and should be inside an `if (Flags.cameraDeviceSetup())` check (or annotate the surrounding method `getCameraDeviceSetupUnsafe` with `@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP) to transfer requirement to caller`)"
+ errorLine1=" return new CameraDeviceSetupImpl(cameraId, /*cameraManager=*/ this, mContext);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/CameraManager.java"
+ line="903"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isCameraDeviceSetupSupported()` is a flagged API and should be inside an `if (Flags.cameraDeviceSetup())` check (or annotate the surrounding method `openCameraDeviceUserAsync` with `@FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP) to transfer requirement to caller`)"
+ errorLine1=" && CameraDeviceSetupImpl.isCameraDeviceSetupSupported(characteristics)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/CameraManager.java"
+ line="983"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `DeviceStateManager` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `registerDeviceStateListener` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" ctx.getSystemService(DeviceStateManager.class).registerCallback("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/CameraManager.java"
+ line="2141"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `registerCallback()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `registerDeviceStateListener` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" ctx.getSystemService(DeviceStateManager.class).registerCallback("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/CameraManager.java"
+ line="2141"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `STATISTICS_LENS_INTRINSICS_SAMPLES` is a flagged API and should be inside an `if (Flags.concertMode())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CONCERT_MODE) to transfer requirement to caller`)"
+ errorLine1=" CaptureResult.STATISTICS_LENS_INTRINSICS_SAMPLES.getNativeKey(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/impl/CameraMetadataNative.java"
+ line="856"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `STATISTICS_LENS_INTRINSICS_SAMPLES` is a flagged API and should be inside an `if (Flags.concertMode())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CONCERT_MODE) to transfer requirement to caller`)"
+ errorLine1=" CaptureResult.STATISTICS_LENS_INTRINSICS_SAMPLES.getNativeKey(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/camera2/impl/CameraMetadataNative.java"
+ line="2021"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8054"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8060"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8066"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8073"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8079"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DOMAIN_PS_3GPP` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" DOMAIN_PS_3GPP,"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8081"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DOMAIN_CS` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" DOMAIN_CS,"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8082"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DOMAIN_PS_NON_3GPP` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" DOMAIN_PS_NON_3GPP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8083"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8085"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DOMAIN_PS_3GPP` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" DOMAIN_PS_3GPP,"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8087"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DOMAIN_CS` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" DOMAIN_CS,"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8088"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DOMAIN_PS_NON_3GPP` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" DOMAIN_PS_NON_3GPP"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8089"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8092"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_NONE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8093"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `VOWIFI_REQUIRES_NONE` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_NONE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8093"
+ column="74"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT, 1);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8094"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_SCAN_TIMER_SEC_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_EMERGENCY_SCAN_TIMER_SEC_INT, 10);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8095"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8096"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REDIAL_TIMER_DISABLED` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8096"
+ column="72"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_NO_PREFERENCE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8097"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SCAN_TYPE_NO_PREFERENCE` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_NO_PREFERENCE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8097"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT, 0);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8098"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8099"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8100"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8101"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8102"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, 120);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8104"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8105"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REDIAL_TIMER_DISABLED` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8105"
+ column="73"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8106"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL` is a flagged API and should be inside an `if (Flags.useOemDomainSelectionService())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="8108"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL` is a flagged API and should be inside an `if (Flags.enableMultipleSaProposals())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="9405"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL` is a flagged API and should be inside an `if (Flags.enableMultipleSaProposals())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS) to transfer requirement to caller`)"
+ errorLine1=" defaults.putBoolean(KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="9406"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.enableAeadAlgorithms())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS) to transfer requirement to caller`)"
+ errorLine1=" KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="9422"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.enableAeadAlgorithms())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS) to transfer requirement to caller`)"
+ errorLine1=" KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="9427"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY` is a flagged API and should be inside an `if (Flags.enableAeadAlgorithms())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS) to transfer requirement to caller`)"
+ errorLine1=" KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="9477"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY` is a flagged API and should be inside an `if (Flags.enableAeadAlgorithms())` check (or annotate the surrounding method `getDefaults` with `@FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS) to transfer requirement to caller`)"
+ errorLine1=" KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="9479"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL` is a flagged API and should be inside an `if (Flags.showCallIdAndCallWaitingInAdditionalSettingsMenu())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="10519"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL` is a flagged API and should be inside an `if (Flags.showCallIdAndCallWaitingInAdditionalSettingsMenu())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="10520"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_PREFER_3G_VISIBILITY_BOOL` is a flagged API and should be inside an `if (Flags.hidePrefer3gItem())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HIDE_PREFER_3G_ITEM) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putBoolean(KEY_PREFER_3G_VISIBILITY_BOOL, true);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="10526"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SHOW_ROAMING_INDICATOR_BOOL` is a flagged API and should be inside an `if (Flags.hideRoamingIcon())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HIDE_ROAMING_ICON) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putBoolean(KEY_SHOW_ROAMING_INDICATOR_BOOL, true);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="10794"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11093"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SATELLITE_ATTACH_SUPPORTED_BOOL` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11095"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 300);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11096"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11097"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putIntArray(KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11105"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putIntArray(KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11113"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11121"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11127"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11128"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_TYPE_MMS` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" NetworkRegistrationInfo.SERVICE_TYPE_MMS"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11134"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL` is a flagged API and should be inside an `if (Flags.businessCallComposer())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11142"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY` is a flagged API and should be inside an `if (Flags.dataOnlyCellularService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11233"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TelephonyRegistryManager` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `registerCarrierConfigChangeListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11771"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `addCarrierConfigChangedListener()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `registerCarrierConfigChangeListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" trm.addCarrierConfigChangedListener(executor, listener);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11775"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TelephonyRegistryManager` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `unregisterCarrierConfigChangeListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11789"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `removeCarrierConfigChangedListener()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `unregisterCarrierConfigChangeListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" trm.removeCarrierConfigChangedListener(listener);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java"
+ line="11793"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `notifyCarrierNetworkChange` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" (TelephonyRegistryManager) this.getSystemService("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/service/carrier/CarrierService.java"
+ line="180"
+ column="14"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `notifyCarrierNetworkChange()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `notifyCarrierNetworkChange` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" telephonyRegistryMgr.notifyCarrierNetworkChange(active);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/service/carrier/CarrierService.java"
+ line="183"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TelephonyRegistryManager` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `notifyCarrierNetworkChange` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" TelephonyRegistryManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/service/carrier/CarrierService.java"
+ line="206"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `notifyCarrierNetworkChange()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `notifyCarrierNetworkChange` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" telephonyRegistryMgr.notifyCarrierNetworkChange(subscriptionId, active);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/service/carrier/CarrierService.java"
+ line="208"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isUsingNonTerrestrialNetwork()` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `updateLevel` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" if (ss != null && ss.isUsingNonTerrestrialNetwork()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java"
+ line="266"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `updateLevel` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java"
+ line="269"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `updateLevel` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java"
+ line="271"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `updateLevel` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java"
+ line="273"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `updateLevel` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/CellSignalStrengthLte.java"
+ line="275"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFrameTimeNanos()` is a flagged API and should be inside an `if (Flags.expectedPresentationTimeApi())` check (or annotate the surrounding method `getFrameTime` with `@FlaggedApi(Flags.FLAG_EXPECTED_PRESENTATION_TIME_API) to transfer requirement to caller`)"
+ errorLine1=" return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/Choreographer.java"
+ line="694"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `lockAnimationClock()` is a flagged API and should be inside an `if (Flags.expectedPresentationTimeReadOnly())` check (or annotate the surrounding method `doFrame` with `@FlaggedApi(Flags.FLAG_EXPECTED_PRESENTATION_TIME_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/Choreographer.java"
+ line="934"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SOURCE_UNKNOWN` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `Condition` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" this(id, summary, "", "", -1, state, SOURCE_UNKNOWN, FLAG_RELEVANT_ALWAYS);"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/Condition.java"
+ line="138"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `Condition()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `Condition` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" this(id, summary, "", "", -1, state, SOURCE_UNKNOWN, FLAG_RELEVANT_ALWAYS);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/Condition.java"
+ line="138"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SOURCE_UNKNOWN` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `Condition` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" this(id, summary, line1, line2, icon, state, SOURCE_UNKNOWN, flags);"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/Condition.java"
+ line="157"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `Condition()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `Condition` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" this(id, summary, line1, line2, icon, state, SOURCE_UNKNOWN, flags);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/Condition.java"
+ line="157"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `Condition()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `Condition` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" this((Uri)source.readParcelable(Condition.class.getClassLoader(), android.net.Uri.class),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/notification/Condition.java"
+ line="192"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SOURCE_UNKNOWN` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `Condition` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" Flags.modesApi() ? source.readInt() : SOURCE_UNKNOWN,"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/Condition.java"
+ line="198"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onCreateConnectionComplete()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `notifyCreateConnectionComplete` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" onCreateConnectionComplete(findConnectionForAction(callId,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/ConnectionService.java"
+ line="2531"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onCreateConferenceComplete()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `notifyCreateConferenceComplete` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" onCreateConferenceComplete(findConferenceForAction(callId,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/ConnectionService.java"
+ line="2548"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getCallDirection()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `addExistingConnection` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" connection.getCallDirection(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/ConnectionService.java"
+ line="3179"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `supportsReliableMessages()` is a flagged API and should be inside an `if (Flags.reliableMessage())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) to transfer requirement to caller`)"
+ errorLine1=" || (other.supportsReliableMessages() == mSupportsReliableMessages))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/location/ContextHubInfo.java"
+ line="365"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isReliable()` is a flagged API and should be inside an `if (Flags.reliableMessage())` check (or annotate the surrounding method `onMessageFromNanoApp` with `@FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) to transfer requirement to caller`)"
+ errorLine1=" && message.isReliable()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/location/ContextHubManager.java"
+ line="715"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_RELIABLE_MESSAGE` is a flagged API and should be inside an `if (Flags.reliableMessage())` check (or annotate the surrounding method `typeToString` with `@FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) to transfer requirement to caller`)"
+ errorLine1=" case ContextHubTransaction.TYPE_RELIABLE_MESSAGE: {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/location/ContextHubTransaction.java"
+ line="235"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `shouldRegisterAttributionSource()` is a flagged API and should be inside an `if (Flags.shouldRegisterAttributionSource())` check (or annotate the surrounding method `ContextImpl` with `@FlaggedApi(Flags.FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE) to transfer requirement to caller`)"
+ errorLine1=" params.getRenouncedPermissions(), params.shouldRegisterAttributionSource(), mDeviceId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ContextImpl.java"
+ line="3540"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_UNSUPPORTED` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `DataCallResponse` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/DataCallResponse.java"
+ line="192"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_UNSUPPORTED` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/DataCallResponse.java"
+ line="655"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_RCS` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `networkCapabilityToApnType` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" return ApnSetting.TYPE_RCS;"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/DataProfile.java"
+ line="435"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `requestNetworkValidation()` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `handleMessage` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" serviceProvider.requestNetworkValidation("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/data/DataService.java"
+ line="744"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER` is a flagged API and should be inside an `if (Flags.headlessDeviceOwnerSingleUserEnabled())` check (or annotate the surrounding method `DeviceAdminInfo` with `@FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/admin/DeviceAdminInfo.java"
+ line="410"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTENT_PROTECTION_DISABLED` is a flagged API and should be inside an `if (Flags.manageDevicePolicyEnabled())` check (or annotate the surrounding method `getContentProtectionPolicy` with `@FlaggedApi(Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return CONTENT_PROTECTION_DISABLED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/admin/DevicePolicyCache.java"
+ line="105"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `equals()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" return baseState.equals(that.baseState)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="101"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `equals()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" && currentState.equals(that.currentState)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="102"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `equals()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `diff` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" if (!baseState.equals(other.baseState)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="120"
+ column="14"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `equals()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `diff` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" if (!currentState.equals(other.currentState)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="123"
+ column="14"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getConfiguration()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" dest.writeTypedObject(supportedStates.get(i).getConfiguration(), flags);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="133"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getConfiguration()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" dest.writeTypedObject(baseState.getConfiguration(), flags);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="136"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getConfiguration()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" dest.writeTypedObject(currentState.getConfiguration(), flags);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="137"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CREATOR` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" DeviceState.Configuration.CREATOR);"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="152"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `DeviceState()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" supportedStates.add(i, new DeviceState(configuration));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="153"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `DeviceState()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" final DeviceState baseState = new DeviceState("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="156"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CREATOR` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" source.readTypedObject(DeviceState.Configuration.CREATOR));"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="157"
+ column="70"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `DeviceState()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" final DeviceState currentState = new DeviceState("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="158"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CREATOR` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" source.readTypedObject(DeviceState.Configuration.CREATOR));"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateInfo.java"
+ line="159"
+ column="70"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onSupportedStatesChanged()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `notifySupportedDeviceStatesChanged` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java"
+ line="393"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onDeviceStateChanged()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `notifyDeviceStateChanged` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" () -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java"
+ line="398"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getConfiguration()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `calculateBaseStateIdentifier` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" DeviceState.Configuration stateConfiguration = currentState.getConfiguration();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateUtil.java"
+ line="45"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getConfiguration()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `calculateBaseStateIdentifier` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" if (stateToCompare.getConfiguration().getPhysicalProperties().isEmpty()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateUtil.java"
+ line="48"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPhysicalProperties()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `calculateBaseStateIdentifier` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" if (stateToCompare.getConfiguration().getPhysicalProperties().isEmpty()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateUtil.java"
+ line="48"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPhysicalProperties()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `calculateBaseStateIdentifier` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" if (isDeviceStateMatchingPhysicalProperties(stateConfiguration.getPhysicalProperties(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateUtil.java"
+ line="51"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getIdentifier()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `calculateBaseStateIdentifier` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" return supportedStates.get(i).getIdentifier();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateUtil.java"
+ line="53"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `INVALID_DEVICE_STATE_IDENTIFIER` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `calculateBaseStateIdentifier` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" return INVALID_DEVICE_STATE_IDENTIFIER;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateUtil.java"
+ line="56"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `hasProperty()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `isDeviceStateMatchingPhysicalProperties` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" if (!state.hasProperty(iterator.next())) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/devicestate/DeviceStateUtil.java"
+ line="69"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getTelephonyDisconnectCause()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" && Objects.equals(mTelephonyDisconnectCause, d.getTelephonyDisconnectCause())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/DisconnectCause.java"
+ line="498"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getTelephonyPreciseDisconnectCause()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" d.getTelephonyPreciseDisconnectCause())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/DisconnectCause.java"
+ line="500"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getImsReasonInfo()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" && Objects.equals(mImsReasonInfo, d.getImsReasonInfo());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/DisconnectCause.java"
+ line="501"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SATELLITE_ENABLED` is a flagged API and should be inside an `if (Flags.oemEnabledSatelliteFlag())` check (or annotate the surrounding method `toString` with `@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" case SATELLITE_ENABLED:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/DisconnectCause.java"
+ line="550"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `dreamCategory` is a flagged API and should be inside an `if (Flags.homePanelDream())` check (or annotate the surrounding method `DreamMetadata` with `@FlaggedApi(Flags.FLAG_HOME_PANEL_DREAM) to transfer requirement to caller`)"
+ errorLine1=" this.dreamCategory = DREAM_CATEGORY_DEFAULT;"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/dreams/DreamService.java"
+ line="1825"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setMinimumFontMetrics()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `reflow` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" b.setText(text, where, where + after)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/text/DynamicLayout.java"
+ line="736"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setShiftDrawingOffsetForStartOverhang()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `reflow` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" b.setText(text, where, where + after)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/text/DynamicLayout.java"
+ line="736"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `reflow` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" b.setText(text, where, where + after)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/text/DynamicLayout.java"
+ line="736"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getTextDirectionHeuristic()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `reflow` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" .setTextDirection(getTextDirectionHeuristic())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/DynamicLayout.java"
+ line="739"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setLocalePreferredLineHeightForMinimumUsed()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `EditText` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" setLocalePreferredLineHeightForMinimumUsed(useLocalePreferredLineHeightForMinimumInt);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/widget/EditText.java"
+ line="150"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `convertSpToDp()` is a flagged API and should be inside an `if (Flags.fontScaleConverterPublic())` check (or annotate the surrounding method `createInterpolatedTableBetween` with `@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) to transfer requirement to caller`)"
+ errorLine1=" float startDp = start.convertSpToDp(sp);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/FontScaleConverterFactory.java"
+ line="242"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `convertSpToDp()` is a flagged API and should be inside an `if (Flags.fontScaleConverterPublic())` check (or annotate the surrounding method `createInterpolatedTableBetween` with `@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) to transfer requirement to caller`)"
+ errorLine1=" float endDp = end.convertSpToDp(sp);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/FontScaleConverterFactory.java"
+ line="243"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.fontScaleConverterPublic())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) to transfer requirement to caller`)"
+ errorLine1="public class FontScaleConverterImpl implements FontScaleConverter {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/FontScaleConverterImpl.java"
+ line="36"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING` is a flagged API and should be inside an `if (Flags.introduceMediaProcessingType())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_INTRODUCE_MEDIA_PROCESSING_TYPE) to transfer requirement to caller`)"
+ errorLine1=" FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ForegroundServiceTypePolicy.java"
+ line="587"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING` is a flagged API and should be inside an `if (Flags.introduceMediaProcessingType())` check (or annotate the surrounding method `DefaultForegroundServiceTypePolicy` with `@FlaggedApi(Flags.FLAG_INTRODUCE_MEDIA_PROCESSING_TYPE) to transfer requirement to caller`)"
+ errorLine1=" mForegroundServiceTypePolicies.put(FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/ForegroundServiceTypePolicy.java"
+ line="1355"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `doCreate` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" new InputTransferToken(), "GameSessionService");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/games/GameSessionService.java"
+ line="127"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CREATOR` is a flagged API and should be inside an `if (Flags.configurableSelectorUiEnabled())` check (or annotate the surrounding method `GetCandidateCredentialsResponse` with `@FlaggedApi(Flags.FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/credentials/GetCandidateCredentialsResponse.java"
+ line="98"
+ column="79"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isConnectionlessStylusHandwritingAvailable()` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `prepareDelegation` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" if (mImm.isConnectionlessStylusHandwritingAvailable()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/HandwritingInitiator.java"
+ line="450"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `startConnectionlessStylusHandwritingForDelegation()` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `prepareDelegation` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" mImm.startConnectionlessStylusHandwritingForDelegation("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/HandwritingInitiator.java"
+ line="454"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" private class DelegationCallback implements ConnectionlessHandwritingCallback {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/HandwritingInitiator.java"
+ line="1116"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONNECTIONLESS_HANDWRITING_ERROR_NO_TEXT_RECOGNIZED` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `onError` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" case CONNECTIONLESS_HANDWRITING_ERROR_NO_TEXT_RECOGNIZED:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/HandwritingInitiator.java"
+ line="1133"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `onError` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" case CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/HandwritingInitiator.java"
+ line="1136"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.scrollFeedbackApi())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SCROLL_FEEDBACK_API) to transfer requirement to caller`)"
+ errorLine1="public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/HapticScrollFeedbackProvider.java"
+ line="36"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getOverlaySupport()` is a flagged API and should be inside an `if (Flags.overlaypropertiesClassApi())` check (or annotate the surrounding method `initDisplayInfo` with `@FlaggedApi(Flags.FLAG_OVERLAYPROPERTIES_CLASS_API) to transfer requirement to caller`)"
+ errorLine1=" final OverlayProperties overlayProperties = defaultDisplay.getOverlaySupport();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/HardwareRenderer.java"
+ line="1394"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isCombinationSupported()` is a flagged API and should be inside an `if (Flags.overlaypropertiesClassApi())` check (or annotate the surrounding method `initDisplayInfo` with `@FlaggedApi(Flags.FLAG_OVERLAYPROPERTIES_CLASS_API) to transfer requirement to caller`)"
+ errorLine1=" overlayProperties.isCombinationSupported("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/HardwareRenderer.java"
+ line="1422"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isCombinationSupported()` is a flagged API and should be inside an `if (Flags.overlaypropertiesClassApi())` check (or annotate the surrounding method `initDisplayInfo` with `@FlaggedApi(Flags.FLAG_OVERLAYPROPERTIES_CLASS_API) to transfer requirement to caller`)"
+ errorLine1=" overlayProperties.isCombinationSupported("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/HardwareRenderer.java"
+ line="1424"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `RGBA_10101010` is a flagged API and should be inside an `if (Flags.requestedFormatsV())` check (or annotate the surrounding method `initDisplayInfo` with `@FlaggedApi(Flags.FLAG_REQUESTED_FORMATS_V) to transfer requirement to caller`)"
+ errorLine1=" HardwareBuffer.RGBA_10101010),"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/HardwareRenderer.java"
+ line="1429"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isMixedColorSpacesSupported()` is a flagged API and should be inside an `if (Flags.overlaypropertiesClassApi())` check (or annotate the surrounding method `initDisplayInfo` with `@FlaggedApi(Flags.FLAG_OVERLAYPROPERTIES_CLASS_API) to transfer requirement to caller`)"
+ errorLine1=" overlayProperties.isMixedColorSpacesSupported());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/HardwareRenderer.java"
+ line="1430"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONFIDENCE_LEVEL_VERY_HIGH` is a flagged API and should be inside an `if (Flags.allowHotwordBumpEgress())` check (or annotate the surrounding method `confidenceLevelToString` with `@FlaggedApi(Flags.FLAG_ALLOW_HOTWORD_BUMP_EGRESS) to transfer requirement to caller`)"
+ errorLine1=" case CONFIDENCE_LEVEL_VERY_HIGH:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/voice/HotwordRejectedResult.java"
+ line="120"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `scheduleMediaViewCleanup()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `release` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.scheduleMediaViewCleanup();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="87"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `release()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.release();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="101"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setSurface()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.setSurface((Surface) msg.obj);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="114"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `dispatchSurfaceChanged()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.dispatchSurfaceChanged("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="119"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `createMediaView()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.createMediaView((IBinder) args.arg1, (Rect) args.arg2);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="126"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `relayoutMediaView()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.relayoutMediaView((Rect) msg.obj);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="131"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `removeMediaView()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.removeMediaView(true);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="135"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `startAdService()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.startAdService();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="139"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `stopAdService()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.stopAdService();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="143"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `resetAdService()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.resetAdService();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="147"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `sendCurrentVideoBounds()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.sendCurrentVideoBounds((Rect) msg.obj);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="151"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `sendCurrentChannelUri()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.sendCurrentChannelUri((Uri) msg.obj);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="155"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `sendTrackInfoList()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.sendTrackInfoList((List<TvTrackInfo>) msg.obj);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="159"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `sendCurrentTvInputId()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.sendCurrentTvInputId((String) msg.obj);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="163"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `sendSigningResult()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.sendSigningResult((String) args.arg1, (byte[]) args.arg2);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="168"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `notifyError()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.notifyError((String) args.arg1, (Bundle) args.arg2);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="174"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `notifyTvMessage()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.notifyTvMessage((Integer) args.arg1, (Bundle) args.arg2);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="180"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `notifyTvInputSessionData()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `executeMessage` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSessionImpl.notifyTvInputSessionData((String) args.arg1, (Bundle) args.arg2);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="186"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `dispatchInputEvent()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `onInputEvent` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" int handled = mSessionImpl.dispatchInputEvent(event, this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="309"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DISPATCH_IN_PROGRESS` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `onInputEvent` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" if (handled != TvAdManager.Session.DISPATCH_IN_PROGRESS) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="310"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DISPATCH_HANDLED` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `onInputEvent` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" event, handled == TvAdManager.Session.DISPATCH_HANDLED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/ad/ITvAdSessionWrapper.java"
+ line="312"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onDeregistered()` is a flagged API and should be inside an `if (Flags.emergencyRegistrationState())` check (or annotate the surrounding method `onDeregistered` with `@FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) to transfer requirement to caller`)"
+ errorLine1=" onDeregistered(info, suggestedAction, attributes);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java"
+ line="576"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onTechnologyChangeFailed()` is a flagged API and should be inside an `if (Flags.emergencyRegistrationState())` check (or annotate the surrounding method `onTechnologyChangeFailed` with `@FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) to transfer requirement to caller`)"
+ errorLine1=" onTechnologyChangeFailed(info, attributes);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java"
+ line="703"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ATTR_REGISTRATION_TYPE_EMERGENCY` is a flagged API and should be inside an `if (Flags.emergencyRegistrationState())` check (or annotate the surrounding method `isEmergency` with `@FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) to transfer requirement to caller`)"
+ errorLine1=" & ImsRegistrationAttributes.ATTR_REGISTRATION_TYPE_EMERGENCY) != 0;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java"
+ line="753"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING` is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" Long.numberOfTrailingZeros(CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/ims/ImsService.java"
+ line="186"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING` is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING, "SIMULTANEOUS_CALLING");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/ims/ImsService.java"
+ line="212"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `handleRenderSuggestion` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" new InputTransferToken(hostInputToken), "InlineSuggestionRenderService");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/autofill/InlineSuggestionRenderService.java"
+ line="170"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `ViewBehavior()` is a flagged API and should be inside an `if (Flags.inputDeviceViewBehaviorApi())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API) to transfer requirement to caller`)"
+ errorLine1=" private final ViewBehavior mViewBehavior = new ViewBehavior(this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InputDevice.java"
+ line="99"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `mShouldSmoothScroll` is a flagged API and should be inside an `if (Flags.inputDeviceViewBehaviorApi())` check (or annotate the surrounding method `InputDevice` with `@FlaggedApi(Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API) to transfer requirement to caller`)"
+ errorLine1=" mViewBehavior.mShouldSmoothScroll = in.readBoolean();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InputDevice.java"
+ line="575"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `mShouldSmoothScroll` is a flagged API and should be inside an `if (Flags.inputDeviceViewBehaviorApi())` check (or annotate the surrounding method `setShouldSmoothScroll` with `@FlaggedApi(Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API) to transfer requirement to caller`)"
+ errorLine1=" mViewBehavior.mShouldSmoothScroll = shouldSmoothScroll;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InputDevice.java"
+ line="1207"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `mShouldSmoothScroll` is a flagged API and should be inside an `if (Flags.inputDeviceViewBehaviorApi())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API) to transfer requirement to caller`)"
+ errorLine1=" out.writeBoolean(mViewBehavior.mShouldSmoothScroll);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InputDevice.java"
+ line="1642"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getHandwritingDelegateFlags()` is a flagged API and should be inside an `if (Flags.homeScreenHandwritingDelegator())` check (or annotate the surrounding method `acceptStylusHandwritingDelegation` with `@FlaggedApi(Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR) to transfer requirement to caller`)"
+ errorLine1=" delegateView.getHandwritingDelegateFlags());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="2880"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getHandwritingDelegateFlags()` is a flagged API and should be inside an `if (Flags.homeScreenHandwritingDelegator())` check (or annotate the surrounding method `acceptStylusHandwritingDelegation` with `@FlaggedApi(Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR) to transfer requirement to caller`)"
+ errorLine1=" delegateView, delegatorPackageName, delegateView.getHandwritingDelegateFlags());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="2911"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `acceptStylusHandwritingDelegation()` is a flagged API and should be inside an `if (Flags.homeScreenHandwritingDelegator())` check (or annotate the surrounding method `acceptStylusHandwritingDelegation` with `@FlaggedApi(Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR) to transfer requirement to caller`)"
+ errorLine1=" acceptStylusHandwritingDelegation("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="2940"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onError()` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `onResult` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" executor.execute(() -> callback.onError("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="4808"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONNECTIONLESS_HANDWRITING_ERROR_NO_TEXT_RECOGNIZED` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `onResult` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" .CONNECTIONLESS_HANDWRITING_ERROR_NO_TEXT_RECOGNIZED));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="4810"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onResult()` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `onResult` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" executor.execute(() -> callback.onResult(text));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="4812"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onError()` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `onError` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" executor.execute(() -> callback.onError(errorCode));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java"
+ line="4834"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onStartConnectionlessStylusHandwriting()` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `canStartStylusHandwriting` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" if (onStartConnectionlessStylusHandwriting("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/inputmethodservice/InputMethodService.java"
+ line="1085"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `canStartStylusHandwriting` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/inputmethodservice/InputMethodService.java"
+ line="1096"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONNECTIONLESS_HANDWRITING_ERROR_OTHER` is a flagged API and should be inside an `if (Flags.connectionlessHandwriting())` check (or annotate the surrounding method `finishStylusHandwriting` with `@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING) to transfer requirement to caller`)"
+ errorLine1=" mConnectionlessHandwritingCallback.onError(CONNECTIONLESS_HANDWRITING_ERROR_OTHER);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/inputmethodservice/InputMethodService.java"
+ line="2825"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getAccessibilityBounceKeysThreshold()` is a flagged API and should be inside an `if (Flags.keyboardA11yBounceKeysFlag())` check (or annotate the surrounding method `isAccessibilityBounceKeysEnabled` with `@FlaggedApi(Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG) to transfer requirement to caller`)"
+ errorLine1=" return getAccessibilityBounceKeysThreshold(context) != 0;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/input/InputSettings.java"
+ line="458"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getAccessibilitySlowKeysThreshold()` is a flagged API and should be inside an `if (Flags.keyboardA11ySlowKeysFlag())` check (or annotate the surrounding method `isAccessibilitySlowKeysEnabled` with `@FlaggedApi(Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG) to transfer requirement to caller`)"
+ errorLine1=" return getAccessibilitySlowKeysThreshold(context) != 0;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/input/InputSettings.java"
+ line="542"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND` is a flagged API and should be inside an `if (Flags.customizableWindowHeaders())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CUSTOMIZABLE_WINDOW_HEADERS) to transfer requirement to caller`)"
+ errorLine1=" mask = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InsetsFlags.java"
+ line="76"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND` is a flagged API and should be inside an `if (Flags.customizableWindowHeaders())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CUSTOMIZABLE_WINDOW_HEADERS) to transfer requirement to caller`)"
+ errorLine1=" equals = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InsetsFlags.java"
+ line="77"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `APPEARANCE_LIGHT_CAPTION_BARS` is a flagged API and should be inside an `if (Flags.customizableWindowHeaders())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CUSTOMIZABLE_WINDOW_HEADERS) to transfer requirement to caller`)"
+ errorLine1=" mask = APPEARANCE_LIGHT_CAPTION_BARS,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InsetsFlags.java"
+ line="80"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `APPEARANCE_LIGHT_CAPTION_BARS` is a flagged API and should be inside an `if (Flags.customizableWindowHeaders())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CUSTOMIZABLE_WINDOW_HEADERS) to transfer requirement to caller`)"
+ errorLine1=" equals = APPEARANCE_LIGHT_CAPTION_BARS,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/InsetsFlags.java"
+ line="81"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `countUriRelativeFilterGroups()` is a flagged API and should be inside an `if (Flags.relativeReferenceIntentFilters())` check (or annotate the surrounding method `toLongString` with `@FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) to transfer requirement to caller`)"
+ errorLine1=" if (Flags.relativeReferenceIntentFilters() && countUriRelativeFilterGroups() > 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/IntentFilter.java"
+ line="577"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `matchGroupsToUri()` is a flagged API and should be inside an `if (Flags.relativeReferenceIntentFilters())` check (or annotate the surrounding method `matchRelRefGroups` with `@FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS) to transfer requirement to caller`)"
+ errorLine1=" return UriRelativeFilterGroup.matchGroupsToUri(mUriRelativeFilterGroups, data);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/IntentFilter.java"
+ line="1839"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isPrivateProfile()` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `getPrivateProfile` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" if (userInfo.isPrivateProfile()) return userInfo;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/app/IntentForwarderActivity.java"
+ line="631"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `uptimeNanos()` is a flagged API and should be inside an `if (Flags.adpfGpuReportActualWorkDuration())` check (or annotate the surrounding method `postEventLogToWorkerThread` with `@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) to transfer requirement to caller`)"
+ errorLine1=" final long realtimeNanos = SystemClock.uptimeNanos();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/jank/InteractionJankMonitor.java"
+ line="606"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `KEYCODE_SCREENSHOT` is a flagged API and should be inside an `if (Flags.emojiAndScreenshotKeycodesAvailable())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE) to transfer requirement to caller`)"
+ errorLine1=" public static final int LAST_KEYCODE = KEYCODE_SCREENSHOT;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/KeyEvent.java"
+ line="955"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isMgf1DigestsSpecified()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `Builder` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" if (sourceSpec.isMgf1DigestsSpecified()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore/KeyGenParameterSpec.java"
+ line="1025"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getMgf1Digests()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `Builder` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" mMgf1Digests = sourceSpec.getMgf1Digests();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore/KeyGenParameterSpec.java"
+ line="1026"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `BIOMETRIC_NO_AUTHENTICATION` is a flagged API and should be inside an `if (Flags.lastAuthenticationTime())` check (or annotate the surrounding method `getLastAuthTime` with `@FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME) to transfer requirement to caller`)"
+ errorLine1=" return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/KeyStoreAuthorization.java"
+ line="139"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `BIOMETRIC_NO_AUTHENTICATION` is a flagged API and should be inside an `if (Flags.lastAuthenticationTime())` check (or annotate the surrounding method `getLastAuthTime` with `@FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME) to transfer requirement to caller`)"
+ errorLine1=" return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/KeyStoreAuthorization.java"
+ line="145"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DATA_BLOCK_SERVICE` is a flagged API and should be inside an `if (Flags.frpEnforcement())` check (or annotate the surrounding method `createConfirmFactoryResetCredentialIntent` with `@FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) to transfer requirement to caller`)"
+ errorLine1=" ServiceManager.getService(Context.PERSISTENT_DATA_BLOCK_SERVICE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/KeyguardManager.java"
+ line="365"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isPrivateProfile()` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `getProfiles` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" && mUserManager.isPrivateProfile())) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/LauncherApps.java"
+ line="712"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `computeDrawingBoundingBox()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `draw` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" RectF drawingRect = computeDrawingBoundingBox();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/Layout.java"
+ line="498"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getLineSpacingMultiplier()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `getSpacingMultiplier` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" return getLineSpacingMultiplier();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/Layout.java"
+ line="4341"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getLineSpacingAmount()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `getSpacingAdd` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" return getLineSpacingAmount();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/Layout.java"
+ line="4368"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" private @LineBreakStyle int mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="276"
+ column="71"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_WORD_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfig.LINE_BREAK_WORD_STYLE_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="280"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HYPHENATION_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" private @Hyphenation int mHyphenation = LineBreakConfig.HYPHENATION_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="282"
+ column="65"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `reset` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" mLineBreakStyle = LINE_BREAK_STYLE_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="343"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_WORD_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `reset` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="344"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HYPHENATION_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `reset` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" mHyphenation = HYPHENATION_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="345"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_STYLE_AUTO` is a flagged API and should be inside an `if (Flags.wordStyleAuto())` check (or annotate the surrounding method `getResolvedLineBreakStyle` with `@FlaggedApi(Flags.FLAG_WORD_STYLE_AUTO) to transfer requirement to caller`)"
+ errorLine1=" defaultStyle = LINE_BREAK_STYLE_AUTO;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="492"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `getResolvedLineBreakStyle` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" return config.mLineBreakStyle == LINE_BREAK_STYLE_UNSPECIFIED"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="499"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_WORD_STYLE_AUTO` is a flagged API and should be inside an `if (Flags.wordStyleAuto())` check (or annotate the surrounding method `getResolvedLineBreakWordStyle` with `@FlaggedApi(Flags.FLAG_WORD_STYLE_AUTO) to transfer requirement to caller`)"
+ errorLine1=" defaultWordStyle = LINE_BREAK_WORD_STYLE_AUTO;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="527"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_WORD_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `getResolvedLineBreakWordStyle` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" return config.mLineBreakWordStyle == LINE_BREAK_WORD_STYLE_UNSPECIFIED"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="534"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HYPHENATION_ENABLED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `getResolvedHyphenation` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" return HYPHENATION_ENABLED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="559"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HYPHENATION_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `getResolvedHyphenation` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" return config.mHyphenation == HYPHENATION_UNSPECIFIED"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="561"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HYPHENATION_ENABLED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `getResolvedHyphenation` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" ? HYPHENATION_ENABLED : config.mHyphenation;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/LineBreakConfig.java"
+ line="562"
+ column="19"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getSessionId()` is a flagged API and should be inside an `if (Flags.loudnessConfiguratorApi())` check (or annotate the surrounding method `dispatchLoudnessCodecParameterChange` with `@FlaggedApi(Flags.FLAG_LOUDNESS_CONFIGURATOR_API) to transfer requirement to caller`)"
+ errorLine1=" if (lcConfig.getSessionId() == sessionId) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/LoudnessCodecDispatcher.java"
+ line="85"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `mediaCodecsConsume()` is a flagged API and should be inside an `if (Flags.loudnessConfiguratorApi())` check (or annotate the surrounding method `dispatchLoudnessCodecParameterChange` with `@FlaggedApi(Flags.FLAG_LOUDNESS_CONFIGURATOR_API) to transfer requirement to caller`)"
+ errorLine1=" lcConfig.mediaCodecsConsume(mcEntry -> {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/media/java/android/media/LoudnessCodecDispatcher.java"
+ line="86"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onLoudnessCodecUpdate()` is a flagged API and should be inside an `if (Flags.loudnessConfiguratorApi())` check (or annotate the surrounding method `dispatchLoudnessCodecParameterChange` with `@FlaggedApi(Flags.FLAG_LOUDNESS_CONFIGURATOR_API) to transfer requirement to caller`)"
+ errorLine1=" l.onLoudnessCodecUpdate(mediaCodec,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/media/java/android/media/LoudnessCodecDispatcher.java"
+ line="110"
+ column="53"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.provider.user_keys")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="29"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.provider.user_keys")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="29"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.provider.user_keys")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="29"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.provider.user_keys")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="29"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="552"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="561"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="992"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.net.platform.flags.register_nsd_offload_engine")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1055"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.quarantined_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1101"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.quarantined_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1101"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.screen_recording_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1310"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.screen_recording_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1310"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.screen_recording_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1310"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.screen_recording_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1310"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.screen_recording_callbacks")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1310"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.frp_enforcement")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1434"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.frp_enforcement")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1434"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1639"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1726"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1726"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1726"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1726"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1839"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="1848"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2292"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2300"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.security_log_v2_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2363"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.security_log_v2_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2363"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.security_log_v2_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2363"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.security_log_v2_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2363"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.security_log_v2_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2363"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.device_theft_api_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2489"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.device_theft_api_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2489"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2523"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2523"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2523"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.esim_management_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2532"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.esim_management_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2532"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.esim_management_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2532"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.esim_management_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2532"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2539"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2546"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2553"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2596"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2596"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2596"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2661"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.always_update_wallpaper_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2904"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.always_update_wallpaper_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="2904"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.feature.flags.enable_read_dropbox_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="3364"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.feature.flags.enable_read_dropbox_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="3364"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.feature.flags.enable_read_dropbox_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="3364"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.accessibility.motion_event_observing")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="3584"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.media.tv.flags.enable_ad_service_fw")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="3971"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.get_resolved_apk_path")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4252"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.get_resolved_apk_path")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4252"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.device_presence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4465"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.device_presence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4465"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.flags.companion_transport_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4475"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.flags.companion_transport_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4475"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.flags.companion_transport_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4475"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.flags.companion_transport_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4475"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.flags.companion_transport_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4475"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.flags.companion_transport_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4475"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4780"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4780"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4780"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4780"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4780"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4780"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4780"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.usage.report_usage_stats_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4938"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.usage.report_usage_stats_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4938"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.usage.report_usage_stats_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4938"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.usage.report_usage_stats_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="4938"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5392"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.sensitive_notification_app_protection")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5535"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5706"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5714"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.contextualsearch.flags.enable_service")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5829"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.contextualsearch.flags.enable_service")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5829"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.introduce_media_processing_type")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5935"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.introduce_media_processing_type")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5935"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.introduce_media_processing_type")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="5935"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.voice_activation_permission_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6084"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.voice_activation_permission_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6084"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.nfc.enable_nfc_mainline")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6304"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6544"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6552"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6560"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.get_binding_uid_importance")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6660"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.get_binding_uid_importance")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6660"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6676"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.input.flags.override_key_behavior_permission_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6693"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.flags.sensitive_content_app_protection_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6704"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.bic_client")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6715"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.bic_client")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6715"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.system_terms_of_address_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6723"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.system_terms_of_address_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6723"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.app.system_terms_of_address_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6723"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.emergency_install_permission")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6732"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.server.power.feature.flags.enable_early_screen_timeout_detector")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6747"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.fsverity_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/Manifest.java"
+ line="6755"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `LineBreakConfigSpan` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `buildForMeasurement` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfigSpan.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="471"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `LineBreakConfigSpan` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `buildForMeasurement` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfigSpan.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="476"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `LineBreakConfigSpan` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `buildForMeasurement` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfigSpan.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="479"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `LineBreakConfigSpan` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `buildForStaticLayoutInternal` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfigSpan.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="625"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `LineBreakConfigSpan` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `buildForStaticLayoutInternal` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfigSpan.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="630"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `LineBreakConfigSpan` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `buildForStaticLayoutInternal` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfigSpan.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="634"
+ column="56"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onAppendReplacementRun()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyReplacementRun` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" testCallback.onAppendReplacementRun(paint, end - start, width);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="784"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `applyStyleRun` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" | (Paint.TEXT_RUN_FLAG_LEFT_EDGE | Paint.TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="802"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `applyStyleRun` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" | (Paint.TEXT_RUN_FLAG_LEFT_EDGE | Paint.TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="802"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onAppendStyleRun()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyleRun` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" testCallback.onAppendStyleRun(paint, config, end - start, false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="814"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `applyStyleRun` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" | (Paint.TEXT_RUN_FLAG_LEFT_EDGE | Paint.TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="828"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `applyStyleRun` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" | (Paint.TEXT_RUN_FLAG_LEFT_EDGE | Paint.TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="828"
+ column="74"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onAppendStyleRun()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyleRun` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" testCallback.onAppendStyleRun(paint, config, levelEnd - levelStart, isRtl);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="840"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getLineBreakConfig()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyMetricsAffectingSpan` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" mLineBreakConfigBuilder.merge(lbcSpan.getLineBreakConfig());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="888"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `merge()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyMetricsAffectingSpan` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" mLineBreakConfigBuilder.merge(lbcSpan.getLineBreakConfig());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/MeasuredParagraph.java"
+ line="888"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HYPHENATION_ENABLED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `appendStyleRun` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" == LineBreakConfig.HYPHENATION_ENABLED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/text/MeasuredText.java"
+ line="319"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onOutputBuffersAvailable()` is a flagged API and should be inside an `if (Flags.largeAudioFrame())` check (or annotate the surrounding method `handleCallback` with `@FlaggedApi(Flags.FLAG_LARGE_AUDIO_FRAME) to transfer requirement to caller`)"
+ errorLine1=" mCallback.onOutputBuffersAvailable("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/media/java/android/media/MediaCodec.java"
+ line="1985"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONFIGURE_FLAG_DETACHED_SURFACE` is a flagged API and should be inside an `if (Flags.nullOutputSurface())` check (or annotate the surrounding method `configure` with `@FlaggedApi(Flags.FLAG_NULL_OUTPUT_SURFACE) to transfer requirement to caller`)"
+ errorLine1=" if (surface == null && (flags & CONFIGURE_FLAG_DETACHED_SURFACE) != 0 && !canDetach) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaCodec.java"
+ line="2371"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONFIGURE_FLAG_DETACHED_SURFACE` is a flagged API and should be inside an `if (Flags.nullOutputSurface())` check (or annotate the surrounding method `configure` with `@FlaggedApi(Flags.FLAG_NULL_OUTPUT_SURFACE) to transfer requirement to caller`)"
+ errorLine1=" if (surface == null && (flags & CONFIGURE_FLAG_DETACHED_SURFACE) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaCodec.java"
+ line="2422"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FEATURE_DynamicColorAspects` is a flagged API and should be inside an `if (Flags.dynamicColorAspects())` check (or annotate the surrounding method `getDecoderFeatures` with `@FlaggedApi(Flags.FLAG_DYNAMIC_COLOR_ASPECTS) to transfer requirement to caller`)"
+ errorLine1=" features.add(new Feature(FEATURE_DynamicColorAspects, (1 << 8), true));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaCodecInfo.java"
+ line="832"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FEATURE_DetachedSurface` is a flagged API and should be inside an `if (Flags.nullOutputSurface())` check (or annotate the surrounding method `getDecoderFeatures` with `@FlaggedApi(Flags.FLAG_NULL_OUTPUT_SURFACE) to transfer requirement to caller`)"
+ errorLine1=" features.add(new Feature(FEATURE_DetachedSurface, (1 << 9), true));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaCodecInfo.java"
+ line="835"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FEATURE_HlgEditing` is a flagged API and should be inside an `if (Flags.hlgEditing())` check (or annotate the surrounding method `getEncoderFeatures` with `@FlaggedApi(Flags.FLAG_HLG_EDITING) to transfer requirement to caller`)"
+ errorLine1=" features.add(new Feature(FEATURE_HlgEditing, (1 << 6), true));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaCodecInfo.java"
+ line="856"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FEATURE_Roi` is a flagged API and should be inside an `if (Flags.regionOfInterest())` check (or annotate the surrounding method `getEncoderFeatures` with `@FlaggedApi(Flags.FLAG_REGION_OF_INTEREST) to transfer requirement to caller`)"
+ errorLine1=" features.add(new Feature(FEATURE_Roi, (1 << 7), true));"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaCodecInfo.java"
+ line="859"
+ column="46"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_HDMI_ARC` is a flagged API and should be inside an `if (Flags.enableAudioPoliciesDeviceAndBluetoothController())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_HDMI_ARC:"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1030"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_HDMI_EARC` is a flagged API and should be inside an `if (Flags.enableAudioPoliciesDeviceAndBluetoothController())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_HDMI_EARC:"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1032"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_REMOTE_TABLET` is a flagged API and should be inside an `if (Flags.enableNewMediaRoute2InfoTypes())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_REMOTE_TABLET:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1050"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_REMOTE_TABLET_DOCKED` is a flagged API and should be inside an `if (Flags.enableNewMediaRoute2InfoTypes())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_REMOTE_TABLET_DOCKED:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1052"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_REMOTE_COMPUTER` is a flagged API and should be inside an `if (Flags.enableNewMediaRoute2InfoTypes())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_REMOTE_COMPUTER:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1054"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_REMOTE_GAME_CONSOLE` is a flagged API and should be inside an `if (Flags.enableNewMediaRoute2InfoTypes())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_REMOTE_GAME_CONSOLE:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1056"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_REMOTE_CAR` is a flagged API and should be inside an `if (Flags.enableNewMediaRoute2InfoTypes())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_REMOTE_CAR:"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1058"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_REMOTE_SMARTWATCH` is a flagged API and should be inside an `if (Flags.enableNewMediaRoute2InfoTypes())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_REMOTE_SMARTWATCH:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1060"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_REMOTE_SMARTPHONE` is a flagged API and should be inside an `if (Flags.enableNewMediaRoute2InfoTypes())` check (or annotate the surrounding method `getDeviceTypeString` with `@FlaggedApi(Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) to transfer requirement to caller`)"
+ errorLine1=" case TYPE_REMOTE_SMARTPHONE:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1062"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER` is a flagged API and should be inside an `if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses())` check (or annotate the surrounding method `Builder` with `@FlaggedApi(Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) to transfer requirement to caller`)"
+ errorLine1=" mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRoute2Info.java"
+ line="1122"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `OPSTR_MEDIA_ROUTING_CONTROL` is a flagged API and should be inside an `if (Flags.enablePrivilegedRoutingForMediaRoutingControl())` check (or annotate the surrounding method `checkCallerHasOnlyRevocablePermissions` with `@FlaggedApi(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL) to transfer requirement to caller`)"
+ errorLine1=" AppOpsManager.OPSTR_MEDIA_ROUTING_CONTROL,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/MediaRouter2.java"
+ line="457"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY` is a flagged API and should be inside an `if (Flags.businessCallComposer())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER) to transfer requirement to caller`)"
+ errorLine1=" CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY + 1;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/ims/feature/MmTelFeature.java"
+ line="574"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY` is a flagged API and should be inside an `if (Flags.businessCallComposer())` check (or annotate the surrounding method `toString` with `@FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER) to transfer requirement to caller`)"
+ errorLine1=" builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/ims/feature/MmTelFeature.java"
+ line="622"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isReliable()` is a flagged API and should be inside an `if (Flags.reliableMessage())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) to transfer requirement to caller`)"
+ errorLine1=" || (other.isReliable() == mIsReliable))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/location/NanoAppMessage.java"
+ line="269"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getMessageSequenceNumber()` is a flagged API and should be inside an `if (Flags.reliableMessage())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) to transfer requirement to caller`)"
+ errorLine1=" || (other.getMessageSequenceNumber() == mMessageSequenceNumber));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/location/NanoAppMessage.java"
+ line="271"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_TYPE_MMS` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" public static final int LAST_SERVICE_TYPE = SERVICE_TYPE_MMS;"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/NetworkRegistrationInfo.java"
+ line="219"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_TYPE_MMS` is a flagged API and should be inside an `if (Flags.carrierEnabledSatelliteFlag())` check (or annotate the surrounding method `serviceTypeToString` with `@FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" case SERVICE_TYPE_MMS: return "MMS";"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/NetworkRegistrationInfo.java"
+ line="759"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getVibrationEffect()` is a flagged API and should be inside an `if (Flags.notificationChannelVibrationEffectApi())` check (or annotate the surrounding method `writeXml` with `@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) to transfer requirement to caller`)"
+ errorLine1=" if (getVibrationEffect() != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationChannel.java"
+ line="1333"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getVibrationEffect()` is a flagged API and should be inside an `if (Flags.notificationChannelVibrationEffectApi())` check (or annotate the surrounding method `writeXml` with `@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) to transfer requirement to caller`)"
+ errorLine1=" out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationChannel.java"
+ line="1334"
+ column="73"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getVibrationEffect()` is a flagged API and should be inside an `if (Flags.notificationChannelVibrationEffectApi())` check (or annotate the surrounding method `toJson` with `@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) to transfer requirement to caller`)"
+ errorLine1=" if (getVibrationEffect() != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationChannel.java"
+ line="1416"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getVibrationEffect()` is a flagged API and should be inside an `if (Flags.notificationChannelVibrationEffectApi())` check (or annotate the surrounding method `toJson` with `@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) to transfer requirement to caller`)"
+ errorLine1=" record.put(ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationChannel.java"
+ line="1417"
+ column="64"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getVibrationEffect()` is a flagged API and should be inside an `if (Flags.notificationChannelVibrationEffectApi())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) to transfer requirement to caller`)"
+ errorLine1=" && Objects.equals(getVibrationEffect(), that.getVibrationEffect())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationChannel.java"
+ line="1545"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getVibrationEffect()` is a flagged API and should be inside an `if (Flags.notificationChannelVibrationEffectApi())` check (or annotate the surrounding method `equals` with `@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) to transfer requirement to caller`)"
+ errorLine1=" && Objects.equals(getVibrationEffect(), that.getVibrationEffect())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationChannel.java"
+ line="1545"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getVibrationEffect()` is a flagged API and should be inside an `if (Flags.notificationChannelVibrationEffectApi())` check (or annotate the surrounding method `hashCode` with `@FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API) to transfer requirement to caller`)"
+ errorLine1=" mImportanceLockedDefaultApp, mOriginalImportance, getVibrationEffect(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationChannel.java"
+ line="1563"
+ column="67"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `addAutomaticZenRule()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `addAutomaticZenRule` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" return addAutomaticZenRule(automaticZenRule, /* fromUser= */ false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationManager.java"
+ line="1368"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `updateAutomaticZenRule()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `updateAutomaticZenRule` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" return updateAutomaticZenRule(id, automaticZenRule, /* fromUser= */ false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationManager.java"
+ line="1404"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `removeAutomaticZenRule()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `removeAutomaticZenRule` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" return removeAutomaticZenRule(id, /* fromUser= */ false);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/NotificationManager.java"
+ line="1473"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ROLLBACK_USER_IMPACT_LOW` is a flagged API and should be inside an `if (Flags.recoverabilityDetection())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_RECOVERABILITY_DETECTION) to transfer requirement to caller`)"
+ errorLine1=" public int rollbackImpactLevel = PackageManager.ROLLBACK_USER_IMPACT_LOW;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/PackageInstaller.java"
+ line="2789"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `isArchived` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `PackageItemInfo` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" isArchived = orig.isArchived;"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/PackageItemInfo.java"
+ line="204"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `isArchived` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `PackageItemInfo` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" isArchived = orig.isArchived;"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/PackageItemInfo.java"
+ line="204"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `isArchived` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" dest.writeBoolean(isArchived);"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/PackageItemInfo.java"
+ line="470"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `isArchived` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `dumpDebug` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" proto.write(PackageItemInfoProto.IS_ARCHIVED, isArchived);"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/PackageItemInfo.java"
+ line="488"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `isArchived` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `PackageItemInfo` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" isArchived = source.readBoolean();"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/PackageItemInfo.java"
+ line="503"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `onPermissionsChanged` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/PackageManager.java"
+ line="766"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `EXTRA_ARCHIVAL` is a flagged API and should be inside an `if (Flags.archiving())` check (or annotate the surrounding method `doHandlePackageEvent` with `@FlaggedApi(Flags.FLAG_ARCHIVING) to transfer requirement to caller`)"
+ errorLine1=" if (intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false)) {"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/content/PackageMonitor.java"
+ line="491"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ACTION_PACKAGE_UNSTOPPED` is a flagged API and should be inside an `if (Flags.stayStopped())` check (or annotate the surrounding method `doHandlePackageEvent` with `@FlaggedApi(Flags.FLAG_STAY_STOPPED) to transfer requirement to caller`)"
+ errorLine1=" } else if (Intent.ACTION_PACKAGE_UNSTOPPED.equals(action)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/content/PackageMonitor.java"
+ line="582"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `measureText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2587"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `measureText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2587"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `measureText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2626"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `measureText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2626"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `getTextWidths` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2846"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `getTextWidths` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2846"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `getTextWidths` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2936"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `getTextWidths` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" setFlags(getFlags() | (TEXT_RUN_FLAG_LEFT_EDGE | TEXT_RUN_FLAG_RIGHT_EDGE));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Paint.java"
+ line="2936"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isMgf1DigestsSpecified()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" if (mSpec.isMgf1DigestsSpecified()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java"
+ line="98"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getMgf1Digests()` is a flagged API and should be inside an `if (Flags.mgf1DigestSetterV2())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_MGF1_DIGEST_SETTER_V2) to transfer requirement to caller`)"
+ errorLine1=" out.writeStringList(List.copyOf(mSpec.getMgf1Digests()));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java"
+ line="99"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `computeBounds()` is a flagged API and should be inside an `if (Flags.exactComputeBounds())` check (or annotate the surrounding method `computeBounds` with `@FlaggedApi(Flags.FLAG_EXACT_COMPUTE_BOUNDS) to transfer requirement to caller`)"
+ errorLine1=" computeBounds(bounds);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/Path.java"
+ line="310"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onOneTimePermissionSessionTimeout()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `notifyOneTimePermissionSessionTimeout` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" onOneTimePermissionSessionTimeout(packageName, deviceId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionControllerService.java"
+ line="672"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onRevokeSelfPermissionsOnKill()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `revokeSelfPermissionsOnKill` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" onRevokeSelfPermissionsOnKill(packageName, permissions, deviceId,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionControllerService.java"
+ line="776"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `getIndicatorAppOpUsageData` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionManager.java"
+ line="1342"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `getPersistentDeviceId` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionManager.java"
+ line="1951"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onPermissionsChanged()` is a flagged API and should be inside an `if (Flags.deviceAwarePermissionApisEnabled())` check (or annotate the surrounding method `handleMessage` with `@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" mListener.onPermissionsChanged(uid, persistentDeviceId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionManager.java"
+ line="2040"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPersistentDeviceId()` is a flagged API and should be inside an `if (Flags.vdmPublicApis())` check (or annotate the surrounding method `getOpUsageDataForAllDevices` with `@FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) to transfer requirement to caller`)"
+ errorLine1=" persistentDeviceIds.add(virtualDevices.get(num).getPersistentDeviceId());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionUsageHelper.java"
+ line="381"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `getOpUsageDataForAllDevices` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" persistentDeviceIds.add(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionUsageHelper.java"
+ line="383"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `getOpUsagesByDevice` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/permission/PermissionUsageHelper.java"
+ line="492"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `hasSimultaneousCallingRestriction()` is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `Builder` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" if (phoneAccount.hasSimultaneousCallingRestriction()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/PhoneAccount.java"
+ line="585"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getSimultaneousCallingRestriction()` is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `Builder` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" mSimultaneousCallingRestriction = phoneAccount.getSimultaneousCallingRestriction();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/PhoneAccount.java"
+ line="586"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `hasSimultaneousCallingRestriction()` is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `toString` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" if (hasSimultaneousCallingRestriction()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telecomm/java/android/telecom/PhoneAccount.java"
+ line="1292"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `STATE_PLAYBACK_SUPPRESSED` is a flagged API and should be inside an `if (Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange())` check (or annotate the surrounding method `isActive` with `@FlaggedApi(Flags.FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE) to transfer requirement to caller`)"
+ errorLine1=" case PlaybackState.STATE_PLAYBACK_SUPPRESSED:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/session/PlaybackState.java"
+ line="541"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `STATE_PLAYBACK_SUPPRESSED` is a flagged API and should be inside an `if (Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange())` check (or annotate the surrounding method `getStringForStateInt` with `@FlaggedApi(Flags.FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE) to transfer requirement to caller`)"
+ errorLine1=" case STATE_PLAYBACK_SUPPRESSED:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/session/PlaybackState.java"
+ line="587"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="39"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="49"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="49"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="57"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="57"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="94"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="103"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="103"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="115"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="123"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitor.java"
+ line="129"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1="@FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="29"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="31"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="31"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="60"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/PowerMonitorReadings.java"
+ line="72"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_UNSUPPORTED` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `PreciseDataConnectionState` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" .build(), null, NETWORK_VALIDATION_UNSUPPORTED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/PreciseDataConnectionState.java"
+ line="143"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_UNSUPPORTED` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `networkValidationStatusToString` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" case NETWORK_VALIDATION_UNSUPPORTED: return "unsupported";"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/PreciseDataConnectionState.java"
+ line="456"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_NOT_REQUESTED` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `networkValidationStatusToString` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" case NETWORK_VALIDATION_NOT_REQUESTED: return "not requested";"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/PreciseDataConnectionState.java"
+ line="457"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_IN_PROGRESS` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `networkValidationStatusToString` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" case NETWORK_VALIDATION_IN_PROGRESS: return "in progress";"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/PreciseDataConnectionState.java"
+ line="458"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_SUCCESS` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `networkValidationStatusToString` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" case NETWORK_VALIDATION_SUCCESS: return "success";"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/PreciseDataConnectionState.java"
+ line="459"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_FAILURE` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `networkValidationStatusToString` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" case NETWORK_VALIDATION_FAILURE: return "failure";"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/PreciseDataConnectionState.java"
+ line="460"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `NETWORK_VALIDATION_UNSUPPORTED` is a flagged API and should be inside an `if (Flags.networkValidation())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_NETWORK_VALIDATION) to transfer requirement to caller`)"
+ errorLine1=" NETWORK_VALIDATION_UNSUPPORTED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/PreciseDataConnectionState.java"
+ line="508"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_WORD_STYLE_AUTO` is a flagged API and should be inside an `if (Flags.wordStyleAuto())` check (or annotate the surrounding method `createMeasuredParagraphsFromPrecomputedText` with `@FlaggedApi(Flags.FLAG_WORD_STYLE_AUTO) to transfer requirement to caller`)"
+ errorLine1=" if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/PrecomputedText.java"
+ line="461"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `merge()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `createMeasuredParagraphsFromPrecomputedText` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" config = new LineBreakConfig.Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/text/PrecomputedText.java"
+ line="464"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_WORD_STYLE_AUTO` is a flagged API and should be inside an `if (Flags.wordStyleAuto())` check (or annotate the surrounding method `createMeasuredParagraphs` with `@FlaggedApi(Flags.FLAG_WORD_STYLE_AUTO) to transfer requirement to caller`)"
+ errorLine1=" if (config.getLineBreakWordStyle() == LineBreakConfig.LINE_BREAK_WORD_STYLE_AUTO"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/PrecomputedText.java"
+ line="515"
+ column="71"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isResumed()` is a flagged API and should be inside an `if (Flags.enableNfcMainline())` check (or annotate the surrounding method `onListItemClick` with `@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) to transfer requirement to caller`)"
+ errorLine1=" if (!isResumed()) {"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/preference/PreferenceActivity.java"
+ line="1071"
+ column="14"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `PromptContentViewParcelable` is a flagged API and should be inside an `if (Flags.customBiometricPrompt())` check (or annotate the surrounding method `PromptInfo` with `@FlaggedApi(Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT) to transfer requirement to caller`)"
+ errorLine1=" mContentView = in.readParcelable(PromptContentViewParcelable.class.getClassLoader(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java"
+ line="75"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `PromptContentViewParcelable` is a flagged API and should be inside an `if (Flags.customBiometricPrompt())` check (or annotate the surrounding method `PromptInfo` with `@FlaggedApi(Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT) to transfer requirement to caller`)"
+ errorLine1=" PromptContentViewParcelable.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java"
+ line="76"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.customBiometricPrompt())` check (or annotate the surrounding method `isContentViewMoreOptionsButtonUsed` with `@FlaggedApi(Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT) to transfer requirement to caller`)"
+ errorLine1=" && mContentView instanceof PromptContentViewWithMoreOptionsButton;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java"
+ line="214"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.customBiometricPrompt())` check (or annotate the surrounding method `setContentView` with `@FlaggedApi(Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT) to transfer requirement to caller`)"
+ errorLine1=" mContentView = (PromptContentViewParcelable) view;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java"
+ line="258"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.media.tv.flags.enable_ad_service_fw")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="610"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.media.tv.flags.enable_ad_service_fw")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="610"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.media.tv.flags.enable_ad_service_fw")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="610"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.media.tv.flags.enable_ad_service_fw")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="610"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="693"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.asm_restrictions_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="759"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.nfc.nfc_read_polling_loop")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="1300"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.flags.sensitive_content_app_protection_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="2782"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.res.default_locale")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="3023"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="3599"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="3599"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="3599"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="3599"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.res.manifest_flagging")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="4290"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="4870"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="4970"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="4979"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="5032"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.virtual.flags.vdm_custom_ime")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="6857"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.inputmethod.ime_switcher_revamp")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="7587"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.inputmethod.ime_switcher_revamp")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="7587"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.inputmethod.ime_switcher_revamp")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="7587"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.inputmethod.ime_switcher_revamp")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="7587"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.sdk_lib_independence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="9481"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.sdk_lib_independence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="9481"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.sdk_lib_independence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="9481"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.sdk_lib_independence")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="9481"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="10701"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="10727"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="10767"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="10776"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.pm.relative_reference_intent_filters")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="10785"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.content_uri_permission_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="11122"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="12404"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.nfc.nfc_observe_mode")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="12438"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.inputmethod.connectionless_handwriting")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="13485"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="13672"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="15472"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.fix_line_height_for_locale")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="15520"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="16364"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.window.flags.enforce_edge_to_edge")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="16561"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.retail_demo_role_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="18455"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.permission.flags.wallet_role_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="18473"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.content_uri_permission_apis")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="25110"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.asm_restrictions_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="25837"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.security.asm_restrictions_enabled")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="27173"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.inputmethod.ime_switcher_revamp")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="41370"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.companion.virtual.flags.vdm_custom_ime")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="41608"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.inputmethod.ime_switcher_revamp")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="41645"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.content.res.default_locale")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="44145"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.nfc.nfc_read_polling_loop")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="45932"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.nfc.nfc_read_polling_loop")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="45956"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.nfc.nfc_read_polling_loop")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="45970"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.nfc.nfc_read_polling_loop")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="45996"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="53416"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="53416"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="53416"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="53416"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="55227"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.fix_line_height_for_locale")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="55238"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("com.android.text.flags.use_bounds_for_width")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="55249"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.flags.sensitive_content_app_protection_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="62287"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @android.annotation.FlaggedApi("android.view.flags.sensitive_content_app_protection_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/android/R.srcjar!/android/R.java"
+ line="64240"
+ column="36"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `METADATA_KEY_COMMENT_SHORT_DESCRIPTION` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" METADATA_KEYS_TYPE.put(METADATA_KEY_COMMENT_SHORT_DESCRIPTION, METADATA_TYPE_TEXT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/RadioMetadata.java"
+ line="249"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `METADATA_KEY_COMMENT_ACTUAL_TEXT` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" METADATA_KEYS_TYPE.put(METADATA_KEY_COMMENT_ACTUAL_TEXT, METADATA_TYPE_TEXT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/RadioMetadata.java"
+ line="250"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `METADATA_KEY_COMMERCIAL` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" METADATA_KEYS_TYPE.put(METADATA_KEY_COMMERCIAL, METADATA_TYPE_TEXT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/RadioMetadata.java"
+ line="251"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `METADATA_KEY_UFIDS` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" METADATA_KEYS_TYPE.put(METADATA_KEY_UFIDS, METADATA_TYPE_TEXT_ARRAY);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/RadioMetadata.java"
+ line="252"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `METADATA_KEY_HD_STATION_NAME_SHORT` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" METADATA_KEYS_TYPE.put(METADATA_KEY_HD_STATION_NAME_SHORT, METADATA_TYPE_TEXT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/RadioMetadata.java"
+ line="253"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `METADATA_KEY_HD_STATION_NAME_LONG` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" METADATA_KEYS_TYPE.put(METADATA_KEY_HD_STATION_NAME_LONG, METADATA_TYPE_TEXT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/RadioMetadata.java"
+ line="254"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `METADATA_KEY_HD_SUBCHANNELS_AVAILABLE` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" METADATA_KEYS_TYPE.put(METADATA_KEY_HD_SUBCHANNELS_AVAILABLE, METADATA_TYPE_INT);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/RadioMetadata.java"
+ line="255"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isPrivateProfile()` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `fetchPrivateProfileUserHandle` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" if (userInfo.isPrivateProfile()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/app/ResolverActivity.java"
+ line="827"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDefaultLocale()` is a flagged API and should be inside an `if (Flags.defaultLocale())` check (or annotate the surrounding method `updateConfigurationImpl` with `@FlaggedApi(Flags.FLAG_DEFAULT_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" if (Flags.defaultLocale() && (lc.getDefaultLocale() != null)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/ResourcesImpl.java"
+ line="474"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDefaultLocale()` is a flagged API and should be inside an `if (Flags.defaultLocale())` check (or annotate the surrounding method `updateConfigurationImpl` with `@FlaggedApi(Flags.FLAG_DEFAULT_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" if (Flags.defaultLocale() && (lc.getDefaultLocale() != null)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/ResourcesImpl.java"
+ line="515"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ROLLBACK_USER_IMPACT_LOW` is a flagged API and should be inside an `if (Flags.recoverabilityDetection())` check (or annotate the surrounding method `RollbackInfo` with `@FlaggedApi(Flags.FLAG_RECOVERABILITY_DETECTION) to transfer requirement to caller`)"
+ errorLine1=" PackageManager.ROLLBACK_USER_IMPACT_LOW);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/rollback/RollbackInfo.java"
+ line="80"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getTransferReason()` is a flagged API and should be inside an `if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses())` check (or annotate the surrounding method `toString` with `@FlaggedApi(Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) to transfer requirement to caller`)"
+ errorLine1=" .append(getTransferReason())"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/RoutingSessionInfo.java"
+ line="528"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TRANSFER_REASON_FALLBACK` is a flagged API and should be inside an `if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES) to transfer requirement to caller`)"
+ errorLine1=" @TransferReason private int mTransferReason = TRANSFER_REASON_FALLBACK;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/RoutingSessionInfo.java"
+ line="590"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SCREEN_RECORDING_STATE_NOT_VISIBLE` is a flagged API and should be inside an `if (Flags.screenRecordingCallbacks())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS) to transfer requirement to caller`)"
+ errorLine1=" private @ScreenRecordingState int mState = SCREEN_RECORDING_STATE_NOT_VISIBLE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ScreenRecordingCallbacks.java"
+ line="54"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SCREEN_RECORDING_STATE_VISIBLE` is a flagged API and should be inside an `if (Flags.screenRecordingCallbacks())` check (or annotate the surrounding method `onScreenRecordingStateChanged` with `@FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS) to transfer requirement to caller`)"
+ errorLine1=" ? SCREEN_RECORDING_STATE_VISIBLE"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ScreenRecordingCallbacks.java"
+ line="85"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SCREEN_RECORDING_STATE_NOT_VISIBLE` is a flagged API and should be inside an `if (Flags.screenRecordingCallbacks())` check (or annotate the surrounding method `onScreenRecordingStateChanged` with `@FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS) to transfer requirement to caller`)"
+ errorLine1=" : SCREEN_RECORDING_STATE_NOT_VISIBLE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ScreenRecordingCallbacks.java"
+ line="86"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SCREEN_RECORDING_STATE_VISIBLE` is a flagged API and should be inside an `if (Flags.screenRecordingCallbacks())` check (or annotate the surrounding method `addCallback` with `@FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS) to transfer requirement to caller`)"
+ errorLine1=" ? SCREEN_RECORDING_STATE_VISIBLE"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ScreenRecordingCallbacks.java"
+ line="96"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SCREEN_RECORDING_STATE_NOT_VISIBLE` is a flagged API and should be inside an `if (Flags.screenRecordingCallbacks())` check (or annotate the surrounding method `addCallback` with `@FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS) to transfer requirement to caller`)"
+ errorLine1=" : SCREEN_RECORDING_STATE_NOT_VISIBLE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ScreenRecordingCallbacks.java"
+ line="97"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING` is a flagged API and should be inside an `if (Flags.introduceMediaProcessingType())` check (or annotate the surrounding method `foregroundServiceTypeToLabel` with `@FlaggedApi(Flags.FLAG_INTRODUCE_MEDIA_PROCESSING_TYPE) to transfer requirement to caller`)"
+ errorLine1=" case FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/ServiceInfo.java"
+ line="707"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isPrivateProfile()` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `setLaunchUserSpecificMessage` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" if (userInfo != null && userInfo.isPrivateProfile()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/app/SetScreenLockDialogActivity.java"
+ line="146"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/provider/Settings.java"
+ line="665"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.biometrics.face_vhal_feature")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/provider/Settings.java"
+ line="11022"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.biometrics.face_vhal_feature")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/provider/Settings.java"
+ line="11030"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java"
+ line="300"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getInputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `createSurfaceView` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" : attachedSurfaceControl.getInputTransferToken(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/window/SplashScreenView.java"
+ line="342"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `uptimeNanos()` is a flagged API and should be inside an `if (Flags.adpfGpuReportActualWorkDuration())` check (or annotate the surrounding method `getTime` with `@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) to transfer requirement to caller`)"
+ errorLine1=" return SystemClock.uptimeNanos() / 1000;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/com/android/internal/util/StatLogger.java"
+ line="94"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `generate` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" final LineBreaker lineBreaker = new LineBreaker.Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/text/StaticLayout.java"
+ line="823"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `computeDrawingBoundingBox()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `computeDrawingBoundingBox` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" mDrawingBounds = super.computeDrawingBoundingBox();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/StaticLayout.java"
+ line="1578"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `createNoBreakSpan()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" buffer.setSpan(LineBreakConfigSpan.createNoBreakSpan(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="297"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `createNoHyphenationSpan()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" buffer.setSpan(LineBreakConfigSpan.createNoHyphenationSpan(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="301"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" int lbStyle = LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="387"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" int lbWordStyle = LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="403"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" if (lbStyle != LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="415"
+ column="52"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `LINE_BREAK_WORD_STYLE_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" || lbWordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_UNSPECIFIED) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="416"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `LineBreakConfigSpan()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" buffer.setSpan(new LineBreakConfigSpan("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="417"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `HYPHENATION_UNSPECIFIED` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `applyStyles` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" LineBreakConfig.HYPHENATION_UNSPECIFIED)),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/res/StringBlock.java"
+ line="419"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setServiceCapabilities()` is a flagged API and should be inside an `if (Flags.dataOnlyCellularService())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" return new Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionInfo.java"
+ line="957"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setTransferStatus()` is a flagged API and should be inside an `if (Flags.supportPsimToEsimConversion())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION) to transfer requirement to caller`)"
+ errorLine1=" return new Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionInfo.java"
+ line="957"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_CAPABILITY_DATA` is a flagged API and should be inside an `if (Flags.dataOnlyCellularService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1436"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_CAPABILITY_VOICE` is a flagged API and should be inside an `if (Flags.dataOnlyCellularService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1443"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_CAPABILITY_SMS` is a flagged API and should be inside an `if (Flags.dataOnlyCellularService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1450"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_CAPABILITY_DATA` is a flagged API and should be inside an `if (Flags.dataOnlyCellularService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" serviceCapabilityToBitmask(SERVICE_CAPABILITY_DATA);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1457"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `addOnSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1696"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `addOnSubscriptionsChangedListener()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `addOnSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" telephonyRegistryManager.addOnSubscriptionsChangedListener(listener,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1699"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `removeOnSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1726"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `removeOnSubscriptionsChangedListener()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `removeOnSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" telephonyRegistryManager.removeOnSubscriptionsChangedListener(listener);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1729"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `addOnOpportunisticSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1784"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `addOnOpportunisticSubscriptionsChangedListener()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `addOnOpportunisticSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" telephonyRegistryManager.addOnOpportunisticSubscriptionsChangedListener("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1787"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `removeOnOpportunisticSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1808"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `removeOnOpportunisticSubscriptionsChangedListener()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `removeOnOpportunisticSubscriptionsChangedListener` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" telephonyRegistryManager.removeOnOpportunisticSubscriptionsChangedListener(listener);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="1811"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SERVICE_CAPABILITY_VOICE` is a flagged API and should be inside an `if (Flags.dataOnlyCellularService())` check (or annotate the surrounding method `getServiceCapabilitiesSet` with `@FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" for (int i = SERVICE_CAPABILITY_VOICE; i <= SERVICE_CAPABILITY_MAX; i++) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/SubscriptionManager.java"
+ line="4800"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CREATOR` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `SurfacePackage` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" mInputTransferToken = InputTransferToken.CREATOR.createFromParcel(in);"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/SurfaceControlViewHost.java"
+ line="196"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `writeToParcel()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `writeToParcel` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" mInputTransferToken.writeToParcel(out, flags);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/SurfaceControlViewHost.java"
+ line="276"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getInputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `toString` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" return "{inputTransferToken=" + getInputTransferToken() + " remoteInterface=""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/SurfaceControlViewHost.java"
+ line="309"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `SurfaceControlViewHost` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" this(context, display, hostToken == null ? null : new InputTransferToken(hostToken),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/SurfaceControlViewHost.java"
+ line="352"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `transferTouchGesture()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `transferTouchGestureToHost` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" return wm.transferTouchGesture(getInputTransferToken(), mWm.mHostInputTransferToken);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/SurfaceControlViewHost.java"
+ line="602"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getInputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `requestEmbeddedFocus` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" mSurfacePackage.getInputTransferToken(), gainFocus);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/SurfaceView.java"
+ line="2155"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `uptimeNanos()` is a flagged API and should be inside an `if (Flags.adpfGpuReportActualWorkDuration())` check (or annotate the surrounding method `uptimeMillis$ravenwood` with `@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) to transfer requirement to caller`)"
+ errorLine1=" return uptimeNanos() / 1_000_000;"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/SystemClock.java"
+ line="202"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `uptimeNanos()` is a flagged API and should be inside an `if (Flags.adpfGpuReportActualWorkDuration())` check (or annotate the surrounding method `elapsedRealtimeNanos$ravenwood` with `@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) to transfer requirement to caller`)"
+ errorLine1=" return uptimeNanos() + (DateUtils.HOUR_IN_MILLIS * 1_000_000);"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/SystemClock.java"
+ line="276"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/health/SystemHealthManager.java"
+ line="231"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="@FlaggedApi should specify an actual flag constant; raw strings are discouraged (and more importantly, **not enforced**)"
+ errorLine1=" @FlaggedApi("com.android.server.power.optimization.power_monitor_api")"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/health/SystemHealthManager.java"
+ line="287"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `null()` is a flagged API and should be inside an `if (Flags.vdmPublicApis())` check (or annotate the surrounding method `setupVirtualDeviceListener` with `@FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) to transfer requirement to caller`)"
+ errorLine1=" mVirtualDeviceListener = new VirtualDeviceManager.VirtualDeviceListener() {"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/hardware/SystemSensorManager.java"
+ line="612"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.vdmPublicApis())` check (or annotate the surrounding method `setupVirtualDeviceListener` with `@FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) to transfer requirement to caller`)"
+ errorLine1=" mVirtualDeviceListener = new VirtualDeviceManager.VirtualDeviceListener() {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/SystemSensorManager.java"
+ line="612"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `registerVirtualDeviceListener()` is a flagged API and should be inside an `if (Flags.vdmPublicApis())` check (or annotate the surrounding method `setupVirtualDeviceListener` with `@FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) to transfer requirement to caller`)"
+ errorLine1=" mVdm.registerVirtualDeviceListener(mContext.getMainExecutor(), mVirtualDeviceListener);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/SystemSensorManager.java"
+ line="627"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `SecurityStateManager` is a flagged API and should be inside an `if (Flags.securityStateService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.SECURITY_STATE_SERVICE, SecurityStateManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="676"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SECURITY_STATE_SERVICE` is a flagged API and should be inside an `if (Flags.securityStateService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.SECURITY_STATE_SERVICE, SecurityStateManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="676"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SECURITY_STATE_SERVICE` is a flagged API and should be inside an `if (Flags.securityStateService())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" Context.SECURITY_STATE_SERVICE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="682"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `SecurityStateManager()` is a flagged API and should be inside an `if (Flags.securityStateService())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" return new SecurityStateManager(service);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="684"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TelephonyRegistryManager` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.TELEPHONY_REGISTRY_SERVICE, TelephonyRegistryManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="743"
+ column="61"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `TelephonyRegistryManager()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" return new TelephonyRegistryManager(ctx);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="747"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TvAdManager` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.TV_AD_SERVICE, TvAdManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1019"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TV_AD_SERVICE` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.TV_AD_SERVICE, TvAdManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1019"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TV_AD_SERVICE` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" ServiceManager.getServiceOrThrow(Context.TV_AD_SERVICE);"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1025"
+ column="74"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `TvAdManager()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" return new TvAdManager(service, ctx.getUserId());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1028"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `PersistentDataBlockManager` is a flagged API and should be inside an `if (Flags.frpEnforcement())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1067"
+ column="64"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DATA_BLOCK_SERVICE` is a flagged API and should be inside an `if (Flags.frpEnforcement())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1067"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DATA_BLOCK_SERVICE` is a flagged API and should be inside an `if (Flags.frpEnforcement())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) to transfer requirement to caller`)"
+ errorLine1=" IBinder b = ServiceManager.getServiceOrThrow(Context.PERSISTENT_DATA_BLOCK_SERVICE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1071"
+ column="70"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `PersistentDataBlockManager()` is a flagged API and should be inside an `if (Flags.frpEnforcement())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT) to transfer requirement to caller`)"
+ errorLine1=" return new PersistentDataBlockManager(persistentDataBlockService);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1075"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `ContextualSearchManager` is a flagged API and should be inside an `if (Flags.enableService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.CONTEXTUAL_SEARCH_SERVICE, ContextualSearchManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1319"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTEXTUAL_SEARCH_SERVICE` is a flagged API and should be inside an `if (Flags.enableService())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.CONTEXTUAL_SEARCH_SERVICE, ContextualSearchManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1319"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTEXTUAL_SEARCH_SERVICE` is a flagged API and should be inside an `if (Flags.enableService())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_ENABLE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" IBinder b = ServiceManager.getService(Context.CONTEXTUAL_SEARCH_SERVICE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1324"
+ column="71"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `ContextualSearchManager()` is a flagged API and should be inside an `if (Flags.enableService())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_ENABLE_SERVICE) to transfer requirement to caller`)"
+ errorLine1=" return b == null ? null : new ContextualSearchManager();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1325"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `DeviceStateManager` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.DEVICE_STATE_SERVICE, DeviceStateManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1564"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `DeviceStateManager()` is a flagged API and should be inside an `if (Flags.deviceStatePropertyApi())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_DEVICE_STATE_PROPERTY_API) to transfer requirement to caller`)"
+ errorLine1=" return new DeviceStateManager();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1568"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `OnDeviceIntelligenceManager` is a flagged API and should be inside an `if (Flags.enableOnDeviceIntelligence())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.ON_DEVICE_INTELLIGENCE_SERVICE, OnDeviceIntelligenceManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1638"
+ column="65"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ON_DEVICE_INTELLIGENCE_SERVICE` is a flagged API and should be inside an `if (Flags.enableOnDeviceIntelligence())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.ON_DEVICE_INTELLIGENCE_SERVICE, OnDeviceIntelligenceManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1638"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `ON_DEVICE_INTELLIGENCE_SERVICE` is a flagged API and should be inside an `if (Flags.enableOnDeviceIntelligence())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) to transfer requirement to caller`)"
+ errorLine1=" Context.ON_DEVICE_INTELLIGENCE_SERVICE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1644"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `OnDeviceIntelligenceManager()` is a flagged API and should be inside an `if (Flags.enableOnDeviceIntelligence())` check (or annotate the surrounding method `createService` with `@FlaggedApi(Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE) to transfer requirement to caller`)"
+ errorLine1=" return new OnDeviceIntelligenceManager(ctx.getOuterContext(), manager);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1647"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `E2eeContactKeysManager` is a flagged API and should be inside an `if (Flags.userKeys())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_USER_KEYS) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.CONTACT_KEYS_SERVICE, E2eeContactKeysManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1670"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTACT_KEYS_SERVICE` is a flagged API and should be inside an `if (Flags.userKeys())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_USER_KEYS) to transfer requirement to caller`)"
+ errorLine1=" registerService(Context.CONTACT_KEYS_SERVICE, E2eeContactKeysManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/SystemServiceRegistry.java"
+ line="1670"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `addView()` is a flagged API and should be inside an `if (Flags.enableArrowIconOnHoverWhenClickable())` check (or annotate the surrounding method `addTab` with `@FlaggedApi(Flags.FLAG_ENABLE_ARROW_ICON_ON_HOVER_WHEN_CLICKABLE) to transfer requirement to caller`)"
+ errorLine1=" mTabWidget.addView(tabIndicator);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/widget/TabHost.java"
+ line="256"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `onSimultaneousCallingStateChanged` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" (SimultaneousCellularCallingSupportListener) mTelephonyCallbackWeakRef.get();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/telephony/TelephonyCallback.java"
+ line="2065"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onSimultaneousCellularCallingSubscriptionsChanged()` is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `onSimultaneousCallingStateChanged` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" () -> listener.onSimultaneousCellularCallingSubscriptionsChanged("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/telephony/TelephonyCallback.java"
+ line="2070"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `SatelliteManager` is a flagged API and should be inside an `if (Flags.oemEnabledSatelliteFlag())` check (or annotate the surrounding method `registerServiceWrappers` with `@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" SatelliteManager.class,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyFrameworkInitializer.java"
+ line="146"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `SatelliteManager()` is a flagged API and should be inside an `if (Flags.oemEnabledSatelliteFlag())` check (or annotate the surrounding method `registerServiceWrappers` with `@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) to transfer requirement to caller`)"
+ errorLine1=" ? new SatelliteManager(context) : null"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyFrameworkInitializer.java"
+ line="148"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `listen` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" (TelephonyRegistryManager)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="6726"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `listenFromListener()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `listen` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" telephonyRegistry.listenFromListener(mSubId, renounceFineLocationAccess,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="6734"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `registerTelephonyCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr = (TelephonyRegistryManager)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="17689"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `registerTelephonyCallback()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `registerTelephonyCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr.registerTelephonyCallback("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="17692"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TelephonyRegistryManager` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `unregisterTelephonyCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="17717"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `unregisterTelephonyCallback()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `unregisterTelephonyCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr.unregisterTelephonyCallback(mSubId, getOpPackageName(),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="17719"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TelephonyRegistryManager` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `registerCarrierPrivilegesCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="18688"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `addCarrierPrivilegesCallback()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `registerCarrierPrivilegesCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr.addCarrierPrivilegesCallback(logicalSlotIndex, executor, callback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="18692"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Class `TelephonyRegistryManager` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `unregisterCarrierPrivilegesCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="18708"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `removeCarrierPrivilegesCallback()` is a flagged API and should be inside an `if (Flags.telecomResolveHiddenDependencies())` check (or annotate the surrounding method `unregisterCarrierPrivilegesCallback` with `@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES) to transfer requirement to caller`)"
+ errorLine1=" mTelephonyRegistryMgr.removeCarrierPrivilegesCallback(callback);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/telephony/java/android/telephony/TelephonyManager.java"
+ line="18712"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="This is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `getEventsFromCallback` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" instanceof TelephonyCallback.SimultaneousCellularCallingSupportListener) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/telephony/TelephonyRegistryManager.java"
+ line="1241"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED` is a flagged API and should be inside an `if (Flags.simultaneousCallingIndications())` check (or annotate the surrounding method `getEventsFromCallback` with `@FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) to transfer requirement to caller`)"
+ errorLine1=" TelephonyCallback.EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/telephony/TelephonyRegistryManager.java"
+ line="1243"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `calculateRunFlag` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" return Paint.TEXT_RUN_FLAG_LEFT_EDGE | Paint.TEXT_RUN_FLAG_RIGHT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="342"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `calculateRunFlag` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" return Paint.TEXT_RUN_FLAG_LEFT_EDGE | Paint.TEXT_RUN_FLAG_RIGHT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="342"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `calculateRunFlag` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" runFlag |= Paint.TEXT_RUN_FLAG_LEFT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="359"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `calculateRunFlag` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" runFlag |= Paint.TEXT_RUN_FLAG_RIGHT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="361"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `calculateRunFlag` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" runFlag |= Paint.TEXT_RUN_FLAG_RIGHT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="366"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `calculateRunFlag` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" runFlag |= Paint.TEXT_RUN_FLAG_LEFT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="368"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `resolveRunFlagForSubSequence` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" if ((runFlag & Paint.TEXT_RUN_FLAG_LEFT_EDGE) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="394"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `resolveRunFlagForSubSequence` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" localRunFlag &= ~Paint.TEXT_RUN_FLAG_LEFT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="398"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `resolveRunFlagForSubSequence` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" localRunFlag &= ~Paint.TEXT_RUN_FLAG_LEFT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="403"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `resolveRunFlagForSubSequence` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" if ((runFlag & Paint.TEXT_RUN_FLAG_RIGHT_EDGE) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="407"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `resolveRunFlagForSubSequence` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" localRunFlag &= ~Paint.TEXT_RUN_FLAG_RIGHT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="411"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `resolveRunFlagForSubSequence` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" localRunFlag &= ~Paint.TEXT_RUN_FLAG_RIGHT_EDGE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="416"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" if ((runFlag & Paint.TEXT_RUN_FLAG_LEFT_EDGE) == Paint.TEXT_RUN_FLAG_LEFT_EDGE) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1345"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" if ((runFlag & Paint.TEXT_RUN_FLAG_LEFT_EDGE) == Paint.TEXT_RUN_FLAG_LEFT_EDGE) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1345"
+ column="64"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" wp.setFlags(wp.getFlags() | Paint.TEXT_RUN_FLAG_LEFT_EDGE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1346"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_LEFT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" wp.setFlags(wp.getFlags() & ~Paint.TEXT_RUN_FLAG_LEFT_EDGE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1348"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" if ((runFlag & Paint.TEXT_RUN_FLAG_RIGHT_EDGE) == Paint.TEXT_RUN_FLAG_RIGHT_EDGE) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1350"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" if ((runFlag & Paint.TEXT_RUN_FLAG_RIGHT_EDGE) == Paint.TEXT_RUN_FLAG_RIGHT_EDGE) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1350"
+ column="65"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" wp.setFlags(wp.getFlags() | Paint.TEXT_RUN_FLAG_RIGHT_EDGE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1351"
+ column="47"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TEXT_RUN_FLAG_RIGHT_EDGE` is a flagged API and should be inside an `if (Flags.letterSpacingJustification())` check (or annotate the surrounding method `handleText` with `@FlaggedApi(Flags.FLAG_LETTER_SPACING_JUSTIFICATION) to transfer requirement to caller`)"
+ errorLine1=" wp.setFlags(wp.getFlags() & ~Paint.TEXT_RUN_FLAG_RIGHT_EDGE);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextLine.java"
+ line="1353"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CREATOR` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `createFromParcel` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" span = LineBreakConfigSpan.CREATOR.createFromParcel(p);"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/text/TextUtils.java"
+ line="1023"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFontMetricsForLocale()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `getResolvedMinimumFontMetrics` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" mTextPaint.getFontMetricsForLocale(mLocalePreferredFontMetrics);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="10860"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setMinimumFontMetrics()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `makeNewLayout` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="10965"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `makeNewLayout` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="10965"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setLineBreakConfig()` is a flagged API and should be inside an `if (Flags.noBreakNoHyphenationSpan())` check (or annotate the surrounding method `makeSingleLayout` with `@FlaggedApi(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN) to transfer requirement to caller`)"
+ errorLine1=" final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11029"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setMinimumFontMetrics()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `makeSingleLayout` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11029"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `makeSingleLayout` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11029"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setMinimumFontMetrics()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `makeSingleLayout` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11115"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `makeSingleLayout` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11115"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `computeDrawingBoundingBox()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `desired` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" max = Math.max(max, layout.computeDrawingBoundingBox().width());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11181"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDrawingBoundingBox()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `onMeasure` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" RectF bbox = boring.getDrawingBoundingBox();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11275"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setMinimumFontMetrics()` is a flagged API and should be inside an `if (Flags.fixLineHeightForLocale())` check (or annotate the surrounding method `suggestedSizeFitsInSpace` with `@FlaggedApi(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE) to transfer requirement to caller`)"
+ errorLine1=" layoutBuilder.setAlignment(getLayoutAlignment())"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11502"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setUseBoundsForWidth()` is a flagged API and should be inside an `if (Flags.useBoundsForWidth())` check (or annotate the surrounding method `suggestedSizeFitsInSpace` with `@FlaggedApi(Flags.FLAG_USE_BOUNDS_FOR_WIDTH) to transfer requirement to caller`)"
+ errorLine1=" layoutBuilder.setAlignment(getLayoutAlignment())"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/widget/TextView.java"
+ line="11502"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONFIG_FORCE_ANALOG_FM` is a flagged API and should be inside an `if (Flags.hdRadioImproved())` check (or annotate the surrounding method `convertForceAnalogConfigFlag` with `@FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) to transfer requirement to caller`)"
+ errorLine1=" && mTuner.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_FM)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/radio/TunerAdapter.java"
+ line="427"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `notifyTvInputSessionData()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `run` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" mSession.getAdSession().notifyTvInputSessionData(type, data);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/TvInputManager.java"
+ line="1498"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onStopPlayback()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `stopPlayback` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" onStopPlayback(mode);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/TvInputService.java"
+ line="2113"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onResumePlayback()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `resumePlayback` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" onResumePlayback();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/TvInputService.java"
+ line="2120"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onTvAdSessionData()` is a flagged API and should be inside an `if (Flags.enableAdServiceFw())` check (or annotate the surrounding method `notifyTvAdSessionData` with `@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) to transfer requirement to caller`)"
+ errorLine1=" onTvAdSessionData(type, data);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/TvInputService.java"
+ line="2218"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onCertificate()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `sendCertificate` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" onCertificate(host, port, cert);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/interactive/TvInteractiveAppService.java"
+ line="1824"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onVideoFreezeUpdated()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `notifyVideoFreezeUpdated` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" onVideoFreezeUpdated(isFrozen);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/interactive/TvInteractiveAppService.java"
+ line="1889"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onSelectedTrackInfo()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `sendSelectedTrackInfo` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" onSelectedTrackInfo(tracks);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/interactive/TvInteractiveAppService.java"
+ line="1945"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onRequestSelectedTrackInfo()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `onRequestSelectedTrackInfo` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" mCallback.onRequestSelectedTrackInfo(mIAppServiceId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/interactive/TvInteractiveAppView.java"
+ line="1822"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onVideoFreezeUpdated()` is a flagged API and should be inside an `if (Flags.tiafVApis())` check (or annotate the surrounding method `onVideoFreezeUpdated` with `@FlaggedApi(Flags.FLAG_TIAF_V_APIS) to transfer requirement to caller`)"
+ errorLine1=" mCallback.onVideoFreezeUpdated(mInputId, isFrozen);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/media/java/android/media/tv/TvView.java"
+ line="1779"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `convertSpToDp()` is a flagged API and should be inside an `if (Flags.fontScaleConverterPublic())` check (or annotate the surrounding method `applyDimension` with `@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) to transfer requirement to caller`)"
+ errorLine1=" metrics.fontScaleConverter.convertSpToDp(value),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/util/TypedValue.java"
+ line="433"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `convertDpToSp()` is a flagged API and should be inside an `if (Flags.fontScaleConverterPublic())` check (or annotate the surrounding method `deriveDimension` with `@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC) to transfer requirement to caller`)"
+ errorLine1=" return metrics.fontScaleConverter.convertDpToSp(dpValue);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/util/TypedValue.java"
+ line="479"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `grantRuntimePermission` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/UiAutomationConnection.java"
+ line="367"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `PERSISTENT_DEVICE_ID_DEFAULT` is a flagged API and should be inside an `if (Flags.persistentDeviceIdApi())` check (or annotate the surrounding method `revokeRuntimePermission` with `@FlaggedApi(Flags.FLAG_PERSISTENT_DEVICE_ID_API) to transfer requirement to caller`)"
+ errorLine1=" VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId, null);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/app/UiAutomationConnection.java"
+ line="387"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isResumed()` is a flagged API and should be inside an `if (Flags.enableNfcMainline())` check (or annotate the surrounding method `dump` with `@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) to transfer requirement to caller`)"
+ errorLine1=" pw.print(pfx); pw.print("resumed: "); pw.println(mActivity.isResumed());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/translation/UiTranslationController.java"
+ line="219"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `COMPLIANCE_WARNING_INPUT_POWER_LIMITED` is a flagged API and should be inside an `if (Flags.enableUsbDataComplianceWarning())` check (or annotate the surrounding method `complianceWarningsToString` with `@FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) to transfer requirement to caller`)"
+ errorLine1=" case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/usb/UsbPort.java"
+ line="813"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `COMPLIANCE_WARNING_MISSING_DATA_LINES` is a flagged API and should be inside an `if (Flags.enableUsbDataComplianceWarning())` check (or annotate the surrounding method `complianceWarningsToString` with `@FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) to transfer requirement to caller`)"
+ errorLine1=" case UsbPortStatus.COMPLIANCE_WARNING_MISSING_DATA_LINES:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/usb/UsbPort.java"
+ line="816"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `COMPLIANCE_WARNING_ENUMERATION_FAIL` is a flagged API and should be inside an `if (Flags.enableUsbDataComplianceWarning())` check (or annotate the surrounding method `complianceWarningsToString` with `@FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) to transfer requirement to caller`)"
+ errorLine1=" case UsbPortStatus.COMPLIANCE_WARNING_ENUMERATION_FAIL:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/usb/UsbPort.java"
+ line="819"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `COMPLIANCE_WARNING_FLAKY_CONNECTION` is a flagged API and should be inside an `if (Flags.enableUsbDataComplianceWarning())` check (or annotate the surrounding method `complianceWarningsToString` with `@FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) to transfer requirement to caller`)"
+ errorLine1=" case UsbPortStatus.COMPLIANCE_WARNING_FLAKY_CONNECTION:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/usb/UsbPort.java"
+ line="822"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `COMPLIANCE_WARNING_UNRELIABLE_IO` is a flagged API and should be inside an `if (Flags.enableUsbDataComplianceWarning())` check (or annotate the surrounding method `complianceWarningsToString` with `@FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING) to transfer requirement to caller`)"
+ errorLine1=" case UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO:"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/hardware/usb/UsbPort.java"
+ line="825"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `USER_TYPE_PROFILE_MANAGED` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `getDefaultUserType` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" case FLAG_MANAGED_PROFILE: return UserManager.USER_TYPE_PROFILE_MANAGED;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/content/pm/UserInfo.java"
+ line="350"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `USER_TYPE_PROFILE_MANAGED` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `isUserTypeManagedProfile` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" return USER_TYPE_PROFILE_MANAGED.equals(userType);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/UserManager.java"
+ line="3063"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `USER_TYPE_PROFILE_CLONE` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `isUserTypeCloneProfile` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" return USER_TYPE_PROFILE_CLONE.equals(userType);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/UserManager.java"
+ line="3100"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `USER_TYPE_PROFILE_PRIVATE` is a flagged API and should be inside an `if (Flags.allowPrivateProfile())` check (or annotate the surrounding method `isUserTypePrivateProfile` with `@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) to transfer requirement to caller`)"
+ errorLine1=" return USER_TYPE_PROFILE_PRIVATE.equals(userType);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/os/UserManager.java"
+ line="3121"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTENT_SENSITIVITY_AUTO` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" (CONTENT_SENSITIVITY_AUTO | CONTENT_SENSITIVITY_SENSITIVE"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="3929"
+ column="14"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTENT_SENSITIVITY_SENSITIVE` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" (CONTENT_SENSITIVITY_AUTO | CONTENT_SENSITIVITY_SENSITIVE"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="3929"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTENT_SENSITIVITY_NOT_SENSITIVE` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" | CONTENT_SENSITIVITY_NOT_SENSITIVE) << PFLAG4_CONTENT_SENSITIVITY_SHIFT;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="3930"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REQUESTED_FRAME_RATE_CATEGORY_DEFAULT` is a flagged API and should be inside an `if (Flags.toolkitSetFrameRateReadOnly())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="5784"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTENT_SENSITIVITY_AUTO` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `View` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" setContentSensitivity(a.getInt(attr, CONTENT_SENSITIVITY_AUTO));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="6528"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setContentSensitivity()` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `View` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" setContentSensitivity(a.getInt(attr, CONTENT_SENSITIVITY_AUTO));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="6528"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setPendingCredentialRequest()` is a flagged API and should be inside an `if (Flags.autofillCredmanDevIntegration())` check (or annotate the surrounding method `onProvideStructure` with `@FlaggedApi(Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) to transfer requirement to caller`)"
+ errorLine1=" structure.setPendingCredentialRequest("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="9665"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPendingCredentialCallback()` is a flagged API and should be inside an `if (Flags.autofillCredmanDevIntegration())` check (or annotate the surrounding method `onGetCredentialResponse` with `@FlaggedApi(Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) to transfer requirement to caller`)"
+ errorLine1=" if (getPendingCredentialCallback() == null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="10070"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPendingCredentialCallback()` is a flagged API and should be inside an `if (Flags.autofillCredmanDevIntegration())` check (or annotate the surrounding method `onGetCredentialResponse` with `@FlaggedApi(Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) to transfer requirement to caller`)"
+ errorLine1=" getPendingCredentialCallback().onResult(response);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="10074"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPendingCredentialCallback()` is a flagged API and should be inside an `if (Flags.autofillCredmanDevIntegration())` check (or annotate the surrounding method `onGetCredentialException` with `@FlaggedApi(Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) to transfer requirement to caller`)"
+ errorLine1=" if (getPendingCredentialCallback() == null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="10081"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPendingCredentialCallback()` is a flagged API and should be inside an `if (Flags.autofillCredmanDevIntegration())` check (or annotate the surrounding method `onGetCredentialException` with `@FlaggedApi(Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) to transfer requirement to caller`)"
+ errorLine1=" getPendingCredentialCallback().onError(new GetCredentialException(errorType, errorMsg));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="10085"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isContentSensitive()` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `updateSensitiveViewsCountIfNeeded` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" if (appeared && isContentSensitive()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="10597"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setPendingCredentialRequest()` is a flagged API and should be inside an `if (Flags.autofillCredmanDevIntegration())` check (or annotate the surrounding method `populateVirtualStructure` with `@FlaggedApi(Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION) to transfer requirement to caller`)"
+ errorLine1=" structure.setPendingCredentialRequest("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="11106"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `CONTENT_SENSITIVITY_AUTO` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `setAutofillHints` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" if (getContentSensitivity() == CONTENT_SENSITIVITY_AUTO) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="13689"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getContentSensitivity()` is a flagged API and should be inside an `if (Flags.sensitiveContentAppProtectionApi())` check (or annotate the surrounding method `setAutofillHints` with `@FlaggedApi(Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API) to transfer requirement to caller`)"
+ errorLine1=" if (getContentSensitivity() == CONTENT_SENSITIVITY_AUTO) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="13689"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `DRAG_FLAG_GLOBAL_SAME_APPLICATION` is a flagged API and should be inside an `if (Flags.delegateUnhandledDrags())` check (or annotate the surrounding method `startDragAndDrop` with `@FlaggedApi(Flags.FLAG_DELEGATE_UNHANDLED_DRAGS) to transfer requirement to caller`)"
+ errorLine1=" if ((flags & DRAG_FLAG_GLOBAL) != 0 && ((flags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="29019"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getIntentSender()` is a flagged API and should be inside an `if (Flags.delegateUnhandledDrags())` check (or annotate the surrounding method `hasActivityPendingIntents` with `@FlaggedApi(Flags.FLAG_DELEGATE_UNHANDLED_DRAGS) to transfer requirement to caller`)"
+ errorLine1=" if (item.getIntentSender() != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="29187"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getIntentSender()` is a flagged API and should be inside an `if (Flags.delegateUnhandledDrags())` check (or annotate the surrounding method `hasActivityPendingIntents` with `@FlaggedApi(Flags.FLAG_DELEGATE_UNHANDLED_DRAGS) to transfer requirement to caller`)"
+ errorLine1=" final PendingIntent pi = new PendingIntent(item.getIntentSender().getTarget());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="29188"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getIntentSender()` is a flagged API and should be inside an `if (Flags.delegateUnhandledDrags())` check (or annotate the surrounding method `cleanUpPendingIntents` with `@FlaggedApi(Flags.FLAG_DELEGATE_UNHANDLED_DRAGS) to transfer requirement to caller`)"
+ errorLine1=" if (item.getIntentSender() != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="29205"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getIntentSender()` is a flagged API and should be inside an `if (Flags.delegateUnhandledDrags())` check (or annotate the surrounding method `cleanUpPendingIntents` with `@FlaggedApi(Flags.FLAG_DELEGATE_UNHANDLED_DRAGS) to transfer requirement to caller`)"
+ errorLine1=" final PendingIntent pi = new PendingIntent(item.getIntentSender().getTarget());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="29206"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE` is a flagged API and should be inside an `if (Flags.toolkitSetFrameRateReadOnly())` check (or annotate the surrounding method `votePreferredFrameRate` with `@FlaggedApi(Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" case (int) REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE ->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="33991"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REQUESTED_FRAME_RATE_CATEGORY_LOW` is a flagged API and should be inside an `if (Flags.toolkitSetFrameRateReadOnly())` check (or annotate the surrounding method `votePreferredFrameRate` with `@FlaggedApi(Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" case (int) REQUESTED_FRAME_RATE_CATEGORY_LOW ->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="33994"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REQUESTED_FRAME_RATE_CATEGORY_NORMAL` is a flagged API and should be inside an `if (Flags.toolkitSetFrameRateReadOnly())` check (or annotate the surrounding method `votePreferredFrameRate` with `@FlaggedApi(Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" case (int) REQUESTED_FRAME_RATE_CATEGORY_NORMAL ->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="33997"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REQUESTED_FRAME_RATE_CATEGORY_HIGH` is a flagged API and should be inside an `if (Flags.toolkitSetFrameRateReadOnly())` check (or annotate the surrounding method `votePreferredFrameRate` with `@FlaggedApi(Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" case (int) REQUESTED_FRAME_RATE_CATEGORY_HIGH ->"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/View.java"
+ line="34000"
+ column="32"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDesiredHdrHeadroom()` is a flagged API and should be inside an `if (Flags.limitedHdr())` check (or annotate the surrounding method `enableHardwareAcceleration` with `@FlaggedApi(Flags.FLAG_LIMITED_HDR) to transfer requirement to caller`)"
+ errorLine1=" updateColorModeIfNeeded(attrs.getColorMode(), attrs.getDesiredHdrHeadroom());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="2022"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getDesiredHdrHeadroom()` is a flagged API and should be inside an `if (Flags.limitedHdr())` check (or annotate the surrounding method `performTraversals` with `@FlaggedApi(Flags.FLAG_LIMITED_HDR) to transfer requirement to caller`)"
+ errorLine1=" updateColorModeIfNeeded(lp.getColorMode(), lp.getDesiredHdrHeadroom());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="3748"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFrameTimeNanos()` is a flagged API and should be inside an `if (Flags.expectedPresentationTimeApi())` check (or annotate the surrounding method `draw` with `@FlaggedApi(Flags.FLAG_EXPECTED_PRESENTATION_TIME_API) to transfer requirement to caller`)"
+ errorLine1=" mChoreographer.getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="5597"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `uptimeNanos()` is a flagged API and should be inside an `if (Flags.adpfGpuReportActualWorkDuration())` check (or annotate the surrounding method `draw` with `@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) to transfer requirement to caller`)"
+ errorLine1=" long timeNs = SystemClock.uptimeNanos();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="5658"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFrameTimeNanos()` is a flagged API and should be inside an `if (Flags.expectedPresentationTimeApi())` check (or annotate the surrounding method `run` with `@FlaggedApi(Flags.FLAG_EXPECTED_PRESENTATION_TIME_API) to transfer requirement to caller`)"
+ errorLine1=" if (doConsumeBatchedInput(mChoreographer.getFrameTimeNanos())) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="10449"
+ column="43"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `getInputTransferToken` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" return new InputTransferToken(inputToken);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="11736"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFrameRateBoostOnTouchEnabled()` is a flagged API and should be inside an `if (Flags.toolkitSetFrameRateReadOnly())` check (or annotate the surrounding method `getFrameRateBoostOnTouchEnabled` with `@FlaggedApi(Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" return mWindowAttributes.getFrameRateBoostOnTouchEnabled();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="13161"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `isFrameRatePowerSavingsBalanced()` is a flagged API and should be inside an `if (Flags.toolkitSetFrameRateReadOnly())` check (or annotate the surrounding method `isFrameRatePowerSavingsBalanced` with `@FlaggedApi(Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) to transfer requirement to caller`)"
+ errorLine1=" return mWindowAttributes.isFrameRatePowerSavingsBalanced();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/ViewRootImpl.java"
+ line="13193"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `POLICY_TYPE_CAMERA` is a flagged API and should be inside an `if (Flags.virtualCamera())` check (or annotate the surrounding method `hasCustomCameraSupport` with `@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA) to transfer requirement to caller`)"
+ errorLine1=" return mVirtualDevice.getDevicePolicy(POLICY_TYPE_CAMERA) == DEVICE_POLICY_CUSTOM;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/companion/virtual/VirtualDevice.java"
+ line="200"
+ column="51"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `VirtualStylus()` is a flagged API and should be inside an `if (Flags.virtualStylus())` check (or annotate the surrounding method `createVirtualStylus` with `@FlaggedApi(Flags.FLAG_VIRTUAL_STYLUS) to transfer requirement to caller`)"
+ errorLine1=" return new VirtualStylus(config, mVirtualDevice, token);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/companion/virtual/VirtualDeviceInternal.java"
+ line="332"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `VirtualCamera()` is a flagged API and should be inside an `if (Flags.virtualCamera())` check (or annotate the surrounding method `createVirtualCamera` with `@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA) to transfer requirement to caller`)"
+ errorLine1=" return new VirtualCamera(mVirtualDevice, mVirtualDevice.getVirtualCameraId(config),"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/companion/virtual/VirtualDeviceInternal.java"
+ line="381"
+ column="20"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onVirtualDeviceCreated()` is a flagged API and should be inside an `if (Flags.vdmPublicApis())` check (or annotate the surrounding method `onVirtualDeviceCreated` with `@FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) to transfer requirement to caller`)"
+ errorLine1=" mExecutor.execute(() -> mListener.onVirtualDeviceCreated(deviceId));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/companion/virtual/VirtualDeviceManager.java"
+ line="1225"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onVirtualDeviceClosed()` is a flagged API and should be inside an `if (Flags.vdmPublicApis())` check (or annotate the surrounding method `onVirtualDeviceClosed` with `@FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS) to transfer requirement to caller`)"
+ errorLine1=" mExecutor.execute(() -> mListener.onVirtualDeviceClosed(deviceId));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/companion/virtual/VirtualDeviceManager.java"
+ line="1235"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `POLICY_TYPE_CLIPBOARD` is a flagged API and should be inside an `if (Flags.crossDeviceClipboard())` check (or annotate the surrounding method `build` with `@FlaggedApi(Flags.FLAG_CROSS_DEVICE_CLIPBOARD) to transfer requirement to caller`)"
+ errorLine1=" mDevicePolicies.delete(POLICY_TYPE_CLIPBOARD);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/companion/virtual/VirtualDeviceParams.java"
+ line="1170"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `POLICY_TYPE_CAMERA` is a flagged API and should be inside an `if (Flags.virtualCamera())` check (or annotate the surrounding method `build` with `@FlaggedApi(Flags.FLAG_VIRTUAL_CAMERA) to transfer requirement to caller`)"
+ errorLine1=" mDevicePolicies.delete(POLICY_TYPE_CAMERA);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/companion/virtual/VirtualDeviceParams.java"
+ line="1174"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onSecureConnectionProvided()` is a flagged API and should be inside an `if (Flags.enableProvideWearableConnectionApi())` check (or annotate the surrounding method `provideSecureConnection` with `@FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingService.this.onSecureConnectionProvided("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="142"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onDataRequestObserverRegistered()` is a flagged API and should be inside an `if (Flags.enableDataRequestObserverApi())` check (or annotate the surrounding method `registerDataRequestObserver` with `@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingService.this.onDataRequestObserverRegistered("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="193"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onDataRequestObserverUnregistered()` is a flagged API and should be inside an `if (Flags.enableDataRequestObserverApi())` check (or annotate the surrounding method `unregisterDataRequestObserver` with `@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingService.this.onDataRequestObserverUnregistered("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="217"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onStartHotwordRecognition()` is a flagged API and should be inside an `if (Flags.enableHotwordWearableSensingApi())` check (or annotate the surrounding method `startHotwordRecognition` with `@FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingService.this.onStartHotwordRecognition("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="237"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onStopHotwordRecognition()` is a flagged API and should be inside an `if (Flags.enableHotwordWearableSensingApi())` check (or annotate the surrounding method `stopHotwordRecognition` with `@FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingService.this.onStopHotwordRecognition(statusConsumer);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="250"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onValidatedByHotwordDetectionService()` is a flagged API and should be inside an `if (Flags.enableHotwordWearableSensingApi())` check (or annotate the surrounding method `onValidatedByHotwordDetectionService` with `@FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingService.this.onValidatedByHotwordDetectionService();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="256"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onStopHotwordAudioStream()` is a flagged API and should be inside an `if (Flags.enableHotwordWearableSensingApi())` check (or annotate the surrounding method `stopActiveHotwordAudio` with `@FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingService.this.onStopHotwordAudioStream();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="262"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `STATUS_UNSUPPORTED_OPERATION` is a flagged API and should be inside an `if (Flags.enableUnsupportedOperationStatusCode())` check (or annotate the surrounding method `onSecureConnectionProvided` with `@FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE) to transfer requirement to caller`)"
+ errorLine1=" statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="361"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `STATUS_UNSUPPORTED_OPERATION` is a flagged API and should be inside an `if (Flags.enableUnsupportedOperationStatusCode())` check (or annotate the surrounding method `onDataRequestObserverRegistered` with `@FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE) to transfer requirement to caller`)"
+ errorLine1=" statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="429"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `STATUS_UNSUPPORTED_OPERATION` is a flagged API and should be inside an `if (Flags.enableUnsupportedOperationStatusCode())` check (or annotate the surrounding method `onDataRequestObserverUnregistered` with `@FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE) to transfer requirement to caller`)"
+ errorLine1=" statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="457"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REQUEST_BUNDLE_KEY` is a flagged API and should be inside an `if (Flags.enableDataRequestObserverApi())` check (or annotate the surrounding method `createDataRequester` with `@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) to transfer requirement to caller`)"
+ errorLine1=" bundle.putParcelable(WearableSensingDataRequest.REQUEST_BUNDLE_KEY, request);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="673"
+ column="61"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `REQUEST_STATUS_CALLBACK_BUNDLE_KEY` is a flagged API and should be inside an `if (Flags.enableDataRequestObserverApi())` check (or annotate the surrounding method `createDataRequester` with `@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API) to transfer requirement to caller`)"
+ errorLine1=" WearableSensingDataRequest.REQUEST_STATUS_CALLBACK_BUNDLE_KEY,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/wearable/WearableSensingService.java"
+ line="682"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `status` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `loadWebViewNativeLibraryFromPackage` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" if (response.status != LIBLOAD_SUCCESS"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="308"
+ column="22"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `status` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `loadWebViewNativeLibraryFromPackage` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="309"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `status` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `loadWebViewNativeLibraryFromPackage` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" return response.status;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="310"
+ column="29"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `packageInfo` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `loadWebViewNativeLibraryFromPackage` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" if (!response.packageInfo.packageName.equals(packageName)) {"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="312"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `status` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `loadWebViewNativeLibraryFromPackage` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="330"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `status` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `getWebViewContextAndSetProvider` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" if (response.status != LIBLOAD_SUCCESS"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="459"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `status` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `getWebViewContextAndSetProvider` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="460"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `status` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `getWebViewContextAndSetProvider` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" + getWebViewPreparationErrorReason(response.status));"
+ errorLine2=" ~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="462"
+ column="69"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `packageInfo` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `getWebViewContextAndSetProvider` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" response.packageInfo.packageName);"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="469"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `packageInfo` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `getWebViewContextAndSetProvider` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" response.packageInfo.packageName,"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="479"
+ column="30"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `packageInfo` is a flagged API and should be inside an `if (Flags.updateServiceIpcWrapper())` check (or annotate the surrounding method `getWebViewContextAndSetProvider` with `@FlaggedApi(Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER) to transfer requirement to caller`)"
+ errorLine1=" verifyPackageInfo(response.packageInfo, newPackageInfo);"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/webkit/WebViewFactory.java"
+ line="510"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `registerBatchedSurfaceControlInputReceiver` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" InputTransferToken inputTransferToken = new InputTransferToken();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/WindowManagerGlobal.java"
+ line="874"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onInputEvent()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `onInputEvent` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" boolean handled = receiver.onInputEvent(event);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/WindowManagerGlobal.java"
+ line="885"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `registerUnbatchedSurfaceControlInputReceiver` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" InputTransferToken inputTransferToken = new InputTransferToken();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/WindowManagerGlobal.java"
+ line="897"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onInputEvent()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `onInputEvent` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" boolean handled = receiver.onInputEvent(event);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/WindowManagerGlobal.java"
+ line="907"
+ column="55"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `SCREEN_RECORDING_STATE_NOT_VISIBLE` is a flagged API and should be inside an `if (Flags.screenRecordingCallbacks())` check (or annotate the surrounding method `addScreenRecordingCallback` with `@FlaggedApi(Flags.FLAG_SCREEN_RECORDING_CALLBACKS) to transfer requirement to caller`)"
+ errorLine1=" return SCREEN_RECORDING_STATE_NOT_VISIBLE;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/WindowManagerImpl.java"
+ line="594"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" private final InputTransferToken mInputTransferToken = new InputTransferToken();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/WindowlessWindowManager.java"
+ line="94"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `InputTransferToken()` is a flagged API and should be inside an `if (Flags.surfaceControlInputReceiver())` check (or annotate the surrounding method `addToDisplay` with `@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) to transfer requirement to caller`)"
+ errorLine1=" state.mInputTransferToken = new InputTransferToken();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/view/WindowlessWindowManager.java"
+ line="214"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `compressToJpegR()` is a flagged API and should be inside an `if (Flags.yuvImageCompressToUltraHdr())` check (or annotate the surrounding method `compressToJpegR` with `@FlaggedApi(Flags.FLAG_YUV_IMAGE_COMPRESS_TO_ULTRA_HDR) to transfer requirement to caller`)"
+ errorLine1=" return compressToJpegR(sdr, quality, stream, emptyExif);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/graphics/java/android/graphics/YuvImage.java"
+ line="276"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `allowPriorityChannels()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `getDefaultZenPolicy` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" ZenPolicy policy = new ZenPolicy.Builder()"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenModeConfig.java"
+ line="379"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_OTHER` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `ensureManualZenRule` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" newRule.type = AutomaticZenRule.TYPE_OTHER;"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenModeConfig.java"
+ line="435"
+ column="45"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `zenDeviceEffects` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `writeRuleXml` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if (Flags.modesApi() && rule.zenDeviceEffects != null) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenModeConfig.java"
+ line="1206"
+ column="38"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `policyState()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `toNotificationPolicy` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" state = Policy.policyState(areChannelsBypassingDnd,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenModeConfig.java"
+ line="1842"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPriorityChannelsAllowed()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `toNotificationPolicy` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" manualRule.zenPolicy.getPriorityChannelsAllowed() != STATE_DISALLOW);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenModeConfig.java"
+ line="1843"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `TYPE_UNKNOWN` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `?` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" public int type = AutomaticZenRule.TYPE_UNKNOWN;"
+ errorLine2=" ~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenModeConfig.java"
+ line="2482"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPriorityChannelsAllowed()` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `areAllPriorityOnlyRingerSoundsMuted` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" && !(config.areChannelsBypassingDnd && policy.getPriorityChannelsAllowed()"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenModeConfig.java"
+ line="2835"
+ column="60"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `mAllowChannels` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `getAllowedChannels` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" return mAllowChannels;"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="576"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `mAllowChannels` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `allowChannels` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" mZenPolicy.mAllowChannels = channelType;"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1019"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_MESSAGES` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_MESSAGES) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1095"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_CALLS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_CALLS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1098"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_CONVERSATIONS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_CONVERSATIONS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1101"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_ALLOW_CHANNELS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_ALLOW_CHANNELS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1104"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_PRIORITY_CATEGORY_REMINDERS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_PRIORITY_CATEGORY_REMINDERS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1107"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_PRIORITY_CATEGORY_EVENTS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_PRIORITY_CATEGORY_EVENTS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1110"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1113"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_PRIORITY_CATEGORY_ALARMS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_PRIORITY_CATEGORY_ALARMS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1116"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_PRIORITY_CATEGORY_MEDIA` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_PRIORITY_CATEGORY_MEDIA) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1119"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_PRIORITY_CATEGORY_SYSTEM` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_PRIORITY_CATEGORY_SYSTEM) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1122"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1125"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_VISUAL_EFFECT_LIGHTS` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_VISUAL_EFFECT_LIGHTS) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1128"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_VISUAL_EFFECT_PEEK` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_VISUAL_EFFECT_PEEK) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1131"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_VISUAL_EFFECT_STATUS_BAR` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_VISUAL_EFFECT_STATUS_BAR) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1134"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_VISUAL_EFFECT_BADGE` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_VISUAL_EFFECT_BADGE) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1137"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_VISUAL_EFFECT_AMBIENT` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_VISUAL_EFFECT_AMBIENT) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1140"
+ column="24"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FIELD_VISUAL_EFFECT_NOTIFICATION_LIST` is a flagged API and should be inside an `if (Flags.modesApi())` check (or annotate the surrounding method `fieldsToString` with `@FlaggedApi(Flags.FLAG_MODES_API) to transfer requirement to caller`)"
+ errorLine1=" if ((bitmask & FIELD_VISUAL_EFFECT_NOTIFICATION_LIST) != 0) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/core/java/android/service/notification/ZenPolicy.java"
+ line="1143"
+ column="24"/>
+ </issue>
+
</issues>
\ No newline at end of file
diff --git a/location/Android.bp b/location/Android.bp
index 5ba35ac..c0e102a 100644
--- a/location/Android.bp
+++ b/location/Android.bp
@@ -39,6 +39,12 @@
"frameworks/base/core/java",
],
},
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ warning_checks: [
+ "FlaggedApi",
+ ],
+ },
}
platform_compat_config {
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 5f84862..acfe473 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -45,13 +45,6 @@
}
flag {
- name: "gnss_call_stop_before_set_position_mode"
- namespace: "location"
- description: "Flag for calling stop() before setPositionMode()"
- bug: "306874828"
-}
-
-flag {
name: "gnss_api_measurement_request_work_source"
namespace: "location"
description: "Flag for GnssMeasurementRequest WorkSource API"
diff --git a/location/lint-baseline.xml b/location/lint-baseline.xml
new file mode 100644
index 0000000..a5a2e25
--- /dev/null
+++ b/location/lint-baseline.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha08" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha08">
+
+ <issue
+ id="FlaggedApi"
+ message="Method `Builder()` is a flagged API and should be inside an `if (Flags.newGeocoder())` check (or annotate the surrounding method `getFromLocation` with `@FlaggedApi(Flags.FLAG_NEW_GEOCODER) to transfer requirement to caller`)"
+ errorLine1=" new ReverseGeocodeRequest.Builder("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/location/java/android/location/Geocoder.java"
+ line="170"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setCallingAttributionTag()` is a flagged API and should be inside an `if (Flags.newGeocoder())` check (or annotate the surrounding method `getFromLocation` with `@FlaggedApi(Flags.FLAG_NEW_GEOCODER) to transfer requirement to caller`)"
+ errorLine1=" b.setCallingAttributionTag(mContext.getAttributionTag());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/location/java/android/location/Geocoder.java"
+ line="178"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `build()` is a flagged API and should be inside an `if (Flags.newGeocoder())` check (or annotate the surrounding method `getFromLocation` with `@FlaggedApi(Flags.FLAG_NEW_GEOCODER) to transfer requirement to caller`)"
+ errorLine1=" mService.reverseGeocode(b.build(), new GeocodeCallbackImpl(listener));"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/location/java/android/location/Geocoder.java"
+ line="181"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `Builder()` is a flagged API and should be inside an `if (Flags.newGeocoder())` check (or annotate the surrounding method `getFromLocationName` with `@FlaggedApi(Flags.FLAG_NEW_GEOCODER) to transfer requirement to caller`)"
+ errorLine1=" new ForwardGeocodeRequest.Builder("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/location/java/android/location/Geocoder.java"
+ line="322"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `setCallingAttributionTag()` is a flagged API and should be inside an `if (Flags.newGeocoder())` check (or annotate the surrounding method `getFromLocationName` with `@FlaggedApi(Flags.FLAG_NEW_GEOCODER) to transfer requirement to caller`)"
+ errorLine1=" b.setCallingAttributionTag(mContext.getAttributionTag());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/location/java/android/location/Geocoder.java"
+ line="333"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `build()` is a flagged API and should be inside an `if (Flags.newGeocoder())` check (or annotate the surrounding method `getFromLocationName` with `@FlaggedApi(Flags.FLAG_NEW_GEOCODER) to transfer requirement to caller`)"
+ errorLine1=" mService.forwardGeocode(b.build(), new GeocodeCallbackImpl(listener));"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="frameworks/base/location/java/android/location/Geocoder.java"
+ line="336"
+ column="37"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getWorkSource()` is a flagged API and should be inside an `if (Flags.gnssApiMeasurementRequestWorkSource())` check (or annotate the surrounding method `Builder` with `@FlaggedApi(Flags.FLAG_GNSS_API_MEASUREMENT_REQUEST_WORK_SOURCE) to transfer requirement to caller`)"
+ errorLine1=" mWorkSource = request.getWorkSource();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/location/java/android/location/GnssMeasurementRequest.java"
+ line="234"
+ column="27"/>
+ </issue>
+
+</issues>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 386a606c..e134c23 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4069,8 +4069,10 @@
private boolean delegateSoundEffectToVdm(@SystemSoundEffect int effectType) {
if (hasCustomPolicyVirtualDeviceContext()) {
VirtualDeviceManager vdm = getVirtualDeviceManager();
- vdm.playSoundEffect(mOriginalContextDeviceId, effectType);
- return true;
+ if (vdm != null) {
+ vdm.playSoundEffect(mOriginalContextDeviceId, effectType);
+ return true;
+ }
}
return false;
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 3ba0d59..8acaf3b 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5143,9 +5143,9 @@
* of negative QP and positive QP are chosen wisely, the overall viewing experience can be
* improved.
* <p>
- * If byte array size is too small than the expected size, components may ignore the
- * configuration silently. If the byte array exceeds the expected size, components shall use
- * the initial portion and ignore the rest.
+ * If byte array size is smaller than the expected size, components will ignore the
+ * configuration and print an error message. If the byte array exceeds the expected size,
+ * components will use the initial portion and ignore the rest.
* <p>
* The scope of this key is throughout the encoding session until it is reconfigured during
* running state.
@@ -5159,7 +5159,8 @@
* Set the region of interest as QpOffset-Rects on the next queued input frame.
* <p>
* The associated value is a String in the format "Top1,Left1-Bottom1,Right1=Offset1;Top2,
- * Left2-Bottom2,Right2=Offset2;...". Co-ordinates (Top, Left), (Top, Right), (Bottom, Left)
+ * Left2-Bottom2,Right2=Offset2;...". If the configuration doesn't follow this pattern,
+ * it will be ignored. Co-ordinates (Top, Left), (Top, Right), (Bottom, Left)
* and (Bottom, Right) form the vertices of bounding box of region of interest in pixels.
* Pixel (0, 0) points to the top-left corner of the frame. Offset is the suggested
* quantization parameter (QP) offset of the blocks in the bounding box. The bounding box
@@ -5171,9 +5172,10 @@
* negative QP and positive QP are chosen wisely, the overall viewing experience can be
* improved.
* <p>
- * If Roi rect is not valid that is bounding box width is < 0 or bounding box height is < 0,
- * components may ignore the configuration silently. If Roi rect extends outside frame
- * boundaries, then rect shall be clamped to the frame boundaries.
+ * If roi (region of interest) rect is outside the frame boundaries, that is, left < 0 or
+ * top < 0 or right > width or bottom > height, then rect shall be clamped to the frame
+ * boundaries. If roi rect is not valid, that is left > right or top > bottom, then the
+ * parameter setting is ignored.
* <p>
* The scope of this key is throughout the encoding session until it is reconfigured during
* running state.
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 0589c0f12..e048d5c 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -26,6 +26,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.net.Uri;
import android.os.Bundle;
@@ -813,6 +814,34 @@
|| mAllowedPackages.contains(packageName);
}
+ /**
+ * Returns whether this route's type can only be published by the system route provider.
+ *
+ * @see #isSystemRoute()
+ * @hide
+ */
+ // The default case catches all other types.
+ @SuppressLint("SwitchIntDef")
+ public boolean isSystemRouteType() {
+ return switch (mType) {
+ case TYPE_BUILTIN_SPEAKER,
+ TYPE_BLUETOOTH_A2DP,
+ TYPE_DOCK,
+ TYPE_BLE_HEADSET,
+ TYPE_HEARING_AID,
+ TYPE_HDMI,
+ TYPE_HDMI_ARC,
+ TYPE_HDMI_EARC,
+ TYPE_USB_ACCESSORY,
+ TYPE_USB_DEVICE,
+ TYPE_USB_HEADSET,
+ TYPE_WIRED_HEADPHONES,
+ TYPE_WIRED_HEADSET ->
+ true;
+ default -> false;
+ };
+ }
+
/** Returns the route suitability status. */
@SuitabilityStatus
@FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index cce3d4f..a14f1fd 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -475,9 +475,25 @@
*/
public final void notifyRoutes(@NonNull Collection<MediaRoute2Info> routes) {
Objects.requireNonNull(routes, "routes must not be null");
- mProviderInfo = new MediaRoute2ProviderInfo.Builder()
- .addRoutes(routes)
- .build();
+ List<MediaRoute2Info> sanitizedRoutes = new ArrayList<>(routes.size());
+
+ for (MediaRoute2Info route : routes) {
+ if (route.isSystemRouteType()) {
+ Log.w(
+ TAG,
+ "Attempting to add a system route type from a non-system route "
+ + "provider. Overriding type to TYPE_UNKNOWN. Route: "
+ + route);
+ sanitizedRoutes.add(
+ new MediaRoute2Info.Builder(route)
+ .setType(MediaRoute2Info.TYPE_UNKNOWN)
+ .build());
+ } else {
+ sanitizedRoutes.add(route);
+ }
+ }
+
+ mProviderInfo = new MediaRoute2ProviderInfo.Builder().addRoutes(sanitizedRoutes).build();
schedulePublishState();
}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 8945bd1..c4c4102 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -41,6 +41,7 @@
"-Wextra",
"-Wunused",
"-Wunreachable-code",
+ "-Wthread-safety",
],
}
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 44fa677..e91c7a9 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -23,6 +23,7 @@
#include <aidl/android/os/IHintManager.h>
#include <aidl/android/os/IHintSession.h>
#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
#include <android/performance_hint.h>
@@ -111,26 +112,26 @@
// HAL preferred update rate
const int64_t mPreferredRateNanos;
// Target duration for choosing update rate
- int64_t mTargetDurationNanos;
+ int64_t mTargetDurationNanos GUARDED_BY(sHintMutex);
// First target hit timestamp
- int64_t mFirstTargetMetTimestamp;
+ int64_t mFirstTargetMetTimestamp GUARDED_BY(sHintMutex);
// Last target hit timestamp
- int64_t mLastTargetMetTimestamp;
+ int64_t mLastTargetMetTimestamp GUARDED_BY(sHintMutex);
// Last hint reported from sendHint indexed by hint value
- std::vector<int64_t> mLastHintSentTimestamp;
+ std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
// Cached samples
- std::vector<hal::WorkDuration> mActualWorkDurations;
- std::string mSessionName;
- static int64_t sIDCounter;
+ std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
+ std::string mSessionName GUARDED_BY(sHintMutex);
+ static int64_t sIDCounter GUARDED_BY(sHintMutex);
// The most recent set of thread IDs
- std::vector<int32_t> mLastThreadIDs;
- std::optional<hal::SessionConfig> mSessionConfig;
+ std::vector<int32_t> mLastThreadIDs GUARDED_BY(sHintMutex);
+ std::optional<hal::SessionConfig> mSessionConfig GUARDED_BY(sHintMutex);
// Tracing helpers
- void traceThreads(std::vector<int32_t>& tids);
- void tracePowerEfficient(bool powerEfficient);
- void traceActualDuration(int64_t actualDuration);
- void traceBatchSize(size_t batchSize);
- void traceTargetDuration(int64_t targetDuration);
+ void traceThreads(std::vector<int32_t>& tids) REQUIRES(sHintMutex);
+ void tracePowerEfficient(bool powerEfficient) REQUIRES(sHintMutex);
+ void traceActualDuration(int64_t actualDuration) REQUIRES(sHintMutex);
+ void traceBatchSize(size_t batchSize) REQUIRES(sHintMutex);
+ void traceTargetDuration(int64_t targetDuration) REQUIRES(sHintMutex);
};
static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr;
@@ -243,6 +244,12 @@
ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
return EINVAL;
}
+ {
+ std::scoped_lock lock(sHintMutex);
+ if (mTargetDurationNanos == targetDurationNanos) {
+ return 0;
+ }
+ }
ndk::ScopedAStatus ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
if (!ret.isOk()) {
ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 78a5357..d19fa98 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -159,6 +159,10 @@
int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
EXPECT_EQ(0, result);
+ // subsequent call with same target should be ignored but return no error
+ result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
+ EXPECT_EQ(0, result);
+
usleep(2); // Sleep for longer than preferredUpdateRateNanos.
int64_t actualDurationNanos = 20;
std::vector<int64_t> actualDurations;
diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp
index b43f2f16..f7a3537 100644
--- a/native/android/thermal.cpp
+++ b/native/android/thermal.cpp
@@ -99,21 +99,21 @@
: mThermalSvc(std::move(service)), mServiceListener(nullptr) {}
AThermalManager::~AThermalManager() {
- std::unique_lock<std::mutex> listenerLock(mListenerMutex);
-
- mListeners.clear();
- if (mServiceListener != nullptr) {
- bool success = false;
- mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
- mServiceListener = nullptr;
+ {
+ std::scoped_lock<std::mutex> listenerLock(mListenerMutex);
+ mListeners.clear();
+ if (mServiceListener != nullptr) {
+ bool success = false;
+ mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
+ mServiceListener = nullptr;
+ }
}
- listenerLock.unlock();
- std::unique_lock<std::mutex> lock(mThresholdsMutex);
+ std::scoped_lock<std::mutex> lock(mThresholdsMutex);
delete[] mThresholds;
}
status_t AThermalManager::notifyStateChange(int32_t status) {
- std::unique_lock<std::mutex> lock(mListenerMutex);
+ std::scoped_lock<std::mutex> lock(mListenerMutex);
AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
for (auto listener : mListeners) {
@@ -123,7 +123,7 @@
}
status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
- std::unique_lock<std::mutex> lock(mListenerMutex);
+ std::scoped_lock<std::mutex> lock(mListenerMutex);
if (callback == nullptr) {
// Callback can not be nullptr
@@ -157,7 +157,7 @@
}
status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) {
- std::unique_lock<std::mutex> lock(mListenerMutex);
+ std::scoped_lock<std::mutex> lock(mListenerMutex);
auto it = std::remove_if(mListeners.begin(),
mListeners.end(),
@@ -216,7 +216,7 @@
status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result,
size_t *size) {
- std::unique_lock<std::mutex> lock(mThresholdsMutex);
+ std::scoped_lock<std::mutex> lock(mThresholdsMutex);
if (mThresholds == nullptr) {
auto thresholds = std::make_unique<std::vector<float>>();
binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get());
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 746c280..8f16f76 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -23,6 +23,9 @@
cc_library_shared {
name: "libjnigraphics",
+ defaults: [
+ "bug_24465209_workaround",
+ ],
cflags: [
"-Wall",
@@ -47,13 +50,6 @@
static_libs: ["libarect"],
- arch: {
- arm: {
- // TODO: This is to work around b/24465209. Remove after root cause is fixed
- pack_relocations: false,
- ldflags: ["-Wl,--hash-style=both"],
- },
- },
host_supported: true,
target: {
android: {
diff --git a/nfc/lint-baseline.xml b/nfc/lint-baseline.xml
index 1dfdd29..dd7b03d 100644
--- a/nfc/lint-baseline.xml
+++ b/nfc/lint-baseline.xml
@@ -210,4 +210,81 @@
column="23"/>
</issue>
+ <issue
+ id="FlaggedApi"
+ message="Method `NfcOemExtension()` is a flagged API and should be inside an `if (Flags.nfcOemExtension())` check (or annotate the surrounding method `NfcAdapter` with `@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) to transfer requirement to caller`)"
+ errorLine1=" mNfcOemExtension = new NfcOemExtension(mContext, this);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
+ line="909"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)"
+ errorLine1=" && ((pollTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
+ line="1917"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)"
+ errorLine1=" && ((pollTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
+ line="1917"
+ column="65"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)"
+ errorLine1=" || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
+ line="1918"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Field `FLAG_SET_DEFAULT_TECH` is a flagged API and should be inside an `if (Flags.nfcSetDefaultDiscTech())` check (or annotate the surrounding method `setDiscoveryTechnology` with `@FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH) to transfer requirement to caller`)"
+ errorLine1=" || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
+ line="1918"
+ column="66"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onVendorNciResponse()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorResponseReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)"
+ errorLine1=" executor.execute(() -> callback.onVendorNciResponse(gid, oid, payload));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java"
+ line="88"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onVendorNciNotification()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorNotificationReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)"
+ errorLine1=" executor.execute(() -> callback.onVendorNciNotification(gid, oid, payload));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java"
+ line="106"
+ column="44"/>
+ </issue>
+
</issues>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index e3dd4cb..9106721 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -20,7 +20,7 @@
<string name="confirmation_title" msgid="2244241995958340998">"هل تريد السماح لـ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>؟"</string>
<string name="profile_name_watch" msgid="576290739483672360">"الساعة"</string>
<string name="chooser_title_non_profile" msgid="6035023914517087400">"اختيار جهاز ليديره تطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string>
- <string name="chooser_title" msgid="2235819929238267637">"اختيار \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\" لإعداده"</string>
+ <string name="chooser_title" msgid="2235819929238267637">"اختيار \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\" للإعداد"</string>
<string name="summary_watch" msgid="7962014927042971830">"سيتم السماح لهذا التطبيق بمزامنة المعلومات، مثلاً اسم المتصل، والوصول إلى هذه الأذونات على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="confirmation_title_glasses" msgid="8288346850537727333">"هل تريد السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بإدارة <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>؟"</string>
<string name="profile_name_glasses" msgid="3506504967216601277">"جهاز"</string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index f98908c..66ab81b 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -16,10 +16,7 @@
package com.android.companiondevicemanager;
-import static android.companion.CompanionDeviceManager.REASON_CANCELED;
-import static android.companion.CompanionDeviceManager.REASON_DISCOVERY_TIMEOUT;
-import static android.companion.CompanionDeviceManager.REASON_INTERNAL_ERROR;
-import static android.companion.CompanionDeviceManager.REASON_USER_REJECTED;
+import static android.companion.CompanionDeviceManager.RESULT_CANCELED;
import static android.companion.CompanionDeviceManager.RESULT_DISCOVERY_TIMEOUT;
import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR;
import static android.companion.CompanionDeviceManager.RESULT_USER_REJECTED;
@@ -27,6 +24,8 @@
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT;
+import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.LOCK;
+import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.sDiscoveryStarted;
import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICONS;
import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_NAMES;
import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_PERMISSIONS;
@@ -232,8 +231,7 @@
boolean forCancelDialog = intent.getBooleanExtra(EXTRA_FORCE_CANCEL_CONFIRMATION, false);
if (forCancelDialog) {
Slog.i(TAG, "Cancelling the user confirmation");
- cancel(/* discoveryTimeOut */ false, /* userRejected */ false,
- /* internalError */ false);
+ cancel(RESULT_CANCELED);
return;
}
@@ -241,8 +239,14 @@
// yet). We can only "process" one request at a time.
final IAssociationRequestCallback appCallback = IAssociationRequestCallback.Stub
.asInterface(intent.getExtras().getBinder(EXTRA_APPLICATION_CALLBACK));
+
+ if (appCallback == null) {
+ return;
+ }
+ Slog.e(TAG, "More than one AssociationRequests are processing.");
+
try {
- requireNonNull(appCallback).onFailure("Busy.");
+ appCallback.onFailure(RESULT_INTERNAL_ERROR);
} catch (RemoteException ignore) {
}
}
@@ -253,8 +257,7 @@
// TODO: handle config changes without cancelling.
if (!isDone()) {
- cancel(/* discoveryTimeOut */ false,
- /* userRejected */ false, /* internalError */ false); // will finish()
+ cancel(RESULT_CANCELED); // will finish()
}
}
@@ -326,8 +329,11 @@
private void onDiscoveryStateChanged(DiscoveryState newState) {
if (newState == FINISHED_TIMEOUT
&& CompanionDeviceDiscoveryService.getScanResult().getValue().isEmpty()) {
- cancel(/* discoveryTimeOut */ true,
- /* userRejected */ false, /* internalError */ false);
+ synchronized (LOCK) {
+ if (sDiscoveryStarted) {
+ cancel(RESULT_DISCOVERY_TIMEOUT);
+ }
+ }
}
}
@@ -365,7 +371,7 @@
mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data);
}
- private void cancel(boolean discoveryTimeout, boolean userRejected, boolean internalError) {
+ private void cancel(int failureCode) {
if (isDone()) {
Slog.w(TAG, "Already done: " + (mApproved ? "Approved" : "Cancelled"));
return;
@@ -373,35 +379,19 @@
mCancelled = true;
// Stop discovery service if it was used.
- if (!mRequest.isSelfManaged() || discoveryTimeout) {
+ if (!mRequest.isSelfManaged()) {
CompanionDeviceDiscoveryService.stop(this);
}
- final String cancelReason;
- final int resultCode;
- if (userRejected) {
- cancelReason = REASON_USER_REJECTED;
- resultCode = RESULT_USER_REJECTED;
- } else if (discoveryTimeout) {
- cancelReason = REASON_DISCOVERY_TIMEOUT;
- resultCode = RESULT_DISCOVERY_TIMEOUT;
- } else if (internalError) {
- cancelReason = REASON_INTERNAL_ERROR;
- resultCode = RESULT_INTERNAL_ERROR;
- } else {
- cancelReason = REASON_CANCELED;
- resultCode = CompanionDeviceManager.RESULT_CANCELED;
- }
-
// First send callback to the app directly...
try {
- Slog.i(TAG, "Sending onFailure to app due to reason=" + cancelReason);
- mAppCallback.onFailure(cancelReason);
+ Slog.i(TAG, "Sending onFailure to app due to failureCode=" + failureCode);
+ mAppCallback.onFailure(failureCode);
} catch (RemoteException ignore) {
}
// ... then set result and finish ("sending" onActivityResult()).
- setResultAndFinish(null, resultCode);
+ setResultAndFinish(null, failureCode);
}
private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
@@ -446,8 +436,7 @@
}
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Package u" + userId + "/" + packageName + " not found.");
- cancel(/* discoveryTimeout */ false,
- /* userRejected */ false, /* internalError */ true);
+ cancel(RESULT_INTERNAL_ERROR);
return;
}
@@ -568,6 +557,8 @@
updateSingleDeviceUi();
+ if (mRequest.isSkipPrompt()) return;
+
mSummary.setVisibility(View.VISIBLE);
mButtonAllow.setVisibility(View.VISIBLE);
mButtonNotAllow.setVisibility(View.VISIBLE);
@@ -629,7 +620,7 @@
// Disable the button, to prevent more clicks.
v.setEnabled(false);
- cancel(/* discoveryTimeout */ false, /* userRejected */ true, /* internalError */ false);
+ cancel(RESULT_USER_REJECTED);
}
private void onShowHelperDialog(View view) {
@@ -755,7 +746,7 @@
@Override
public void onShowHelperDialogFailed() {
- cancel(/* discoveryTimeout */ false, /* userRejected */ false, /* internalError */ true);
+ cancel(RESULT_INTERNAL_ERROR);
}
@Override
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index e809433..6c1bc4e 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -90,9 +90,6 @@
new MutableLiveData<>(Collections.emptyList());
private static MutableLiveData<DiscoveryState> sStateLiveData =
new MutableLiveData<>(DiscoveryState.NOT_STARTED);
- private static final Object LOCK = new Object();
- @GuardedBy("LOCK")
- private static boolean sDiscoveryStarted = false;
private BluetoothManager mBtManager;
private BluetoothAdapter mBtAdapter;
@@ -109,6 +106,10 @@
private boolean mStopAfterFirstMatch;
+ static final Object LOCK = new Object();
+ @GuardedBy("LOCK")
+ static boolean sDiscoveryStarted = false;
+
/**
* A state enum for devices' discovery.
*/
@@ -127,6 +128,7 @@
return false;
}
}
+ sScanResultsLiveData.setValue(Collections.emptyList());
requireNonNull(associationRequest);
final Intent intent = new Intent(context, CompanionDeviceDiscoveryService.class);
intent.setAction(ACTION_START_DISCOVERY);
@@ -192,7 +194,6 @@
sDiscoveryStarted = true;
}
mStopAfterFirstMatch = request.isSingleDevice();
- sScanResultsLiveData.setValue(Collections.emptyList());
sStateLiveData.setValue(DiscoveryState.IN_PROGRESS);
final List<DeviceFilter<?>> allFilters = request.getDeviceFilters();
diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig
index 80412321..225f8c6 100644
--- a/packages/CrashRecovery/aconfig/flags.aconfig
+++ b/packages/CrashRecovery/aconfig/flags.aconfig
@@ -26,8 +26,8 @@
}
flag {
- name: "reenable_settings_resets"
+ name: "deprecate_flags_and_settings_resets"
namespace: "modularization"
- description: "Re-enables settings resets only, deletes flag resets"
+ description: "Deletes flag and settings resets"
bug: "333847376"
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 5728c8c..35addb3 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -24,7 +24,6 @@
import androidx.activity.viewModels
import com.android.credentialmanager.ui.theme.WearCredentialSelectorTheme
import com.android.credentialmanager.ui.WearApp
-import com.google.android.horologist.annotations.ExperimentalHorologistApi
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint(ComponentActivity::class)
@@ -32,7 +31,6 @@
private val viewModel: CredentialSelectorViewModel by viewModels()
- @OptIn(ExperimentalHorologistApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
Log.d(TAG, "onCreate, intent: $intent")
super.onCreate(savedInstanceState)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
index c641d7f..25bc381 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
@@ -15,6 +15,7 @@
*/
package com.android.credentialmanager.ui.components
+import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Icon
import android.graphics.drawable.Drawable
@@ -22,7 +23,11 @@
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Lock
+import androidx.compose.material.icons.outlined.LockOpen
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -46,7 +51,7 @@
onClick: () -> Unit,
secondaryLabel: String? = null,
icon: Drawable? = null,
- isAuthenticationEntryLocked: Boolean = false,
+ isAuthenticationEntryLocked: Boolean? = null,
textAlign: TextAlign = TextAlign.Center,
modifier: Modifier = Modifier,
colors: ChipColors = ChipDefaults.secondaryChipColors()
@@ -77,7 +82,7 @@
text: @Composable () -> Unit,
secondaryLabel: String? = null,
icon: Drawable? = null,
- isAuthenticationEntryLocked: Boolean = false,
+ isAuthenticationEntryLocked: Boolean? = null,
modifier: Modifier = Modifier,
colors: ChipColors = ChipDefaults.primaryChipColors(),
) {
@@ -94,16 +99,23 @@
text = secondaryLabel,
)
- if (isAuthenticationEntryLocked)
- // TODO(b/324465527) change this to lock icon and correct size once figma mocks are
- // updated
- Icon(
- bitmap = checkNotNull(icon?.toBitmap()?.asImageBitmap()),
- // Decorative purpose only.
- contentDescription = null,
- modifier = Modifier.size(10.dp),
- tint = Color.Unspecified
- )
+ if (isAuthenticationEntryLocked != null) {
+ if (isAuthenticationEntryLocked) {
+ Icon(
+ Icons.Outlined.Lock,
+ contentDescription = null,
+ modifier = Modifier.size(12.dp).align(Alignment.CenterVertically),
+ tint = WearMaterialTheme.colors.onSurfaceVariant
+ )
+ } else {
+ Icon(
+ Icons.Outlined.LockOpen,
+ contentDescription = null,
+ modifier = Modifier.size(12.dp).align(Alignment.CenterVertically),
+ tint = WearMaterialTheme.colors.onSurfaceVariant
+ )
+ }
+ }
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
index 22f6bf0..282fea0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
@@ -22,7 +22,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -57,7 +56,6 @@
text: String,
textAlign: TextAlign = TextAlign.Center,
modifier: Modifier = Modifier,
- onTextLayout: (TextLayoutResult) -> Unit = {},
) {
Text(
modifier = modifier.padding(start = 8.dp, end = 8.dp).wrapContentSize(),
@@ -67,7 +65,6 @@
overflow = TextOverflow.Ellipsis,
textAlign = textAlign,
maxLines = 2,
- onTextLayout = onTextLayout,
)
}
@@ -79,7 +76,6 @@
maxLines: Int = 1,
modifier: Modifier = Modifier,
color: Color = WearMaterialTheme.colors.onSurface,
- onTextLayout: (TextLayoutResult) -> Unit = {},
) {
Text(
modifier = modifier.wrapContentSize(),
@@ -89,7 +85,6 @@
overflow = TextOverflow.Ellipsis,
textAlign = textAlign,
maxLines = maxLines,
- onTextLayout = onTextLayout,
)
}
@@ -97,7 +92,6 @@
fun WearSecondaryLabel(
text: String,
modifier: Modifier = Modifier,
- onTextLayout: (TextLayoutResult) -> Unit = {},
) {
Text(
modifier = modifier.wrapContentSize(),
@@ -107,6 +101,5 @@
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Start,
maxLines = 1,
- onTextLayout = onTextLayout,
)
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index 473094c..2656275 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -43,7 +43,8 @@
var icon: Drawable? = null
// provide icon if all entries have the same provider
- if (sortedEntries.all {it.providerId == sortedEntries[0].providerId}) {
+ if (sortedEntries.isNotEmpty() &&
+ sortedEntries.all {it.providerId == sortedEntries[0].providerId}) {
icon = providerInfos[0].icon
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
index fb81e73..36e9792 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
@@ -75,7 +75,10 @@
CredentialsScreenChip(
label = credential.userName,
onClick = { selectEntry(credential, false) },
- secondaryLabel = credential.credentialTypeDisplayName,
+ secondaryLabel =
+ credential.credentialTypeDisplayName.ifEmpty {
+ credential.providerDisplayName
+ },
icon = credential.icon,
textAlign = TextAlign.Start
)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
index 7addc74..ce2bad0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
@@ -61,13 +61,12 @@
val credentials = credentialSelectorUiState.sortedEntries
item {
var title = stringResource(R.string.choose_sign_in_title)
-
- if (credentials.isEmpty()) {
- title = stringResource(R.string.choose_sign_in_title)
- } else if (credentials.all{ it.credentialType == CredentialType.PASSKEY }) {
- title = stringResource(R.string.choose_passkey_title)
- } else if (credentials.all { it.credentialType == CredentialType.PASSWORD }) {
- title = stringResource(R.string.choose_password_title)
+ if (credentials.isNotEmpty()) {
+ if (credentials.all { it.credentialType == CredentialType.PASSKEY }) {
+ title = stringResource(R.string.choose_passkey_title)
+ } else if (credentials.all { it.credentialType == CredentialType.PASSWORD }) {
+ title = stringResource(R.string.choose_password_title)
+ }
}
SignInHeader(
@@ -77,16 +76,19 @@
}
credentials.forEach { credential: CredentialEntryInfo ->
- item {
- CredentialsScreenChip(
- label = credential.userName,
- onClick = { selectEntry(credential, false) },
- secondaryLabel = credential.credentialTypeDisplayName,
- icon = credential.icon,
- )
- CredentialsScreenChipSpacer()
+ item {
+ CredentialsScreenChip(
+ label = credential.userName,
+ onClick = { selectEntry(credential, false) },
+ secondaryLabel =
+ credential.credentialTypeDisplayName.ifEmpty {
+ credential.providerDisplayName
+ },
+ icon = credential.icon,
+ )
+ CredentialsScreenChipSpacer()
+ }
}
- }
credentialSelectorUiState.authenticationEntryList.forEach { authenticationEntryInfo ->
item {
@@ -96,7 +98,6 @@
CredentialsScreenChipSpacer()
}
}
-
item {
Spacer(modifier = Modifier.size(8.dp))
}
diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp
index baafe7b..a94c8c5 100644
--- a/packages/CtsShim/Android.bp
+++ b/packages/CtsShim/Android.bp
@@ -61,7 +61,6 @@
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_legacy",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v3",
],
@@ -102,7 +101,6 @@
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_legacy",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v3",
],
diff --git a/packages/CtsShim/apk/riscv64/CtsShim.apk b/packages/CtsShim/apk/riscv64/CtsShim.apk
index af306a5..5ab190d 100644
--- a/packages/CtsShim/apk/riscv64/CtsShim.apk
+++ b/packages/CtsShim/apk/riscv64/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/riscv64/CtsShimPriv.apk b/packages/CtsShim/apk/riscv64/CtsShimPriv.apk
index 9a9997d..441f86f 100644
--- a/packages/CtsShim/apk/riscv64/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/riscv64/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index d6b7ecf..5b3d47e 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -93,7 +93,6 @@
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v2_unsigned_payload",
@@ -200,7 +199,6 @@
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v2_unsigned_payload",
diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp
index 2dbf2a2..ac85d2b 100644
--- a/packages/CtsShim/build/jni/Android.bp
+++ b/packages/CtsShim/build/jni/Android.bp
@@ -33,7 +33,6 @@
"com.android.apex.cts.shim.v1",
"com.android.apex.cts.shim.v2",
"com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
- "com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
"com.android.apex.cts.shim.v2_unsigned_payload",
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index 74fd7c5..3fa4bce 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -54,8 +54,6 @@
<string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
<string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
<string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
- <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
- <skip />
- <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
- <skip />
+ <string name="keyboard_layout_serbian_cyrillic" msgid="7013541044323542196">"Serbian (Cyrillic)"</string>
+ <string name="keyboard_layout_montenegrin_cyrillic" msgid="2391253952894077421">"Montenegrin (Cyrillic)"</string>
</resources>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index 777d939..2ae35ab 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -54,8 +54,6 @@
<string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
<string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
<string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
- <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
- <skip />
- <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
- <skip />
+ <string name="keyboard_layout_serbian_cyrillic" msgid="7013541044323542196">"Serbian (Cyrillic)"</string>
+ <string name="keyboard_layout_montenegrin_cyrillic" msgid="2391253952894077421">"Montenegrin (Cyrillic)"</string>
</resources>
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 493818b..d60290e 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -174,7 +174,7 @@
/** Return the content description of footer preference. */
@VisibleForTesting
- CharSequence getContentDescription() {
+ public CharSequence getContentDescription() {
return mContentDescription;
}
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 0ab33b7..98a7290 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -53,6 +53,12 @@
".grey900",
R.color.settingslib_color_grey50);
map.put(
+ ".red100",
+ R.color.settingslib_color_red500);
+ map.put(
+ ".red200",
+ R.color.settingslib_color_red500);
+ map.put(
".red400",
R.color.settingslib_color_red600);
map.put(
@@ -65,14 +71,14 @@
".blue400",
R.color.settingslib_color_blue600);
map.put(
- ".green400",
- R.color.settingslib_color_green600);
+ ".green100",
+ R.color.settingslib_color_green500);
map.put(
".green200",
R.color.settingslib_color_green500);
map.put(
- ".red200",
- R.color.settingslib_color_red500);
+ ".green400",
+ R.color.settingslib_color_green600);
map.put(
".cream",
R.color.settingslib_color_charcoal);
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 5966c9f..e4bc7b4 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -11,3 +11,6 @@
# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
per-file *.xml=*
+
+# Notification-related utilities
+per-file **/notification/** = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 9a344c3..2ae3b56 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -41,23 +41,25 @@
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.TopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.NonRestartableComposable
-import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.AlignmentLine
import androidx.compose.ui.layout.LastBaseline
import androidx.compose.ui.layout.Layout
@@ -66,6 +68,7 @@
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.heading
+import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
@@ -80,11 +83,10 @@
import kotlin.math.max
import kotlin.math.roundToInt
-private val windowInsets: WindowInsets
+private val safeDrawingWindowInsets: WindowInsets
@Composable
@NonRestartableComposable
- get() = WindowInsets.safeDrawing
- .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
+ get() = WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
@Composable
internal fun CustomizedTopAppBar(
@@ -97,14 +99,12 @@
titleTextStyle = MaterialTheme.typography.titleMedium,
navigationIcon = navigationIcon,
actions = actions,
- windowInsets = windowInsets,
+ windowInsets = safeDrawingWindowInsets,
colors = topAppBarColors(),
)
}
-/**
- * The customized LargeTopAppBar for Settings.
- */
+/** The customized LargeTopAppBar for Settings. */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun CustomizedLargeTopAppBar(
@@ -124,7 +124,7 @@
navigationIcon = navigationIcon,
actions = actions,
colors = topAppBarColors(),
- windowInsets = windowInsets,
+ windowInsets = safeDrawingWindowInsets,
pinnedHeight = ContainerHeight,
scrollBehavior = scrollBehavior,
)
@@ -134,38 +134,41 @@
private fun Title(title: String, maxLines: Int = Int.MAX_VALUE) {
Text(
text = title,
- modifier = Modifier.padding(
- start = SettingsDimension.itemPaddingAround,
- end = SettingsDimension.itemPaddingEnd,
- )
- .semantics { heading() },
+ modifier =
+ Modifier.padding(
+ start = SettingsDimension.itemPaddingAround,
+ end = SettingsDimension.itemPaddingEnd,
+ )
+ .semantics { heading() },
overflow = TextOverflow.Ellipsis,
maxLines = maxLines,
)
}
@Composable
-private fun topAppBarColors() = TopAppBarColors(
- containerColor = MaterialTheme.colorScheme.settingsBackground,
- scrolledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
- navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
- titleContentColor = MaterialTheme.colorScheme.onSurface,
- actionIconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
-)
+private fun topAppBarColors() =
+ TopAppBarColors(
+ containerColor = MaterialTheme.colorScheme.settingsBackground,
+ scrolledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
+ navigationIconContentColor = MaterialTheme.colorScheme.onSurface,
+ titleContentColor = MaterialTheme.colorScheme.onSurface,
+ actionIconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
/**
* Represents the colors used by a top app bar in different states.
+ *
* This implementation animates the container color according to the top app bar scroll state. It
* does not animate the leading, headline, or trailing colors.
*
- * @constructor create an instance with arbitrary colors, see [TopAppBarColors] for a
- * factory method using the default material3 spec
* @param containerColor the color used for the background of this BottomAppBar. Use
- * [Color.Transparent] to have no color.
+ * [Color.Transparent] to have no color.
* @param scrolledContainerColor the container color when content is scrolled behind it
* @param navigationIconContentColor the content color used for the navigation icon
* @param titleContentColor the content color used for the title
* @param actionIconContentColor the content color used for actions
+ * @constructor create an instance with arbitrary colors, see [TopAppBarColors] for a factory method
+ * using the default material3 spec
*/
@Stable
private class TopAppBarColors(
@@ -180,11 +183,11 @@
* Represents the container color used for the top app bar.
*
* A [colorTransitionFraction] provides a percentage value that can be used to generate a color.
- * Usually, an app bar implementation will pass in a [colorTransitionFraction] read from
- * the [TopAppBarState.collapsedFraction] or the [TopAppBarState.overlappedFraction].
+ * Usually, an app bar implementation will pass in a [colorTransitionFraction] read from the
+ * [TopAppBarState.collapsedFraction] or the [TopAppBarState.overlappedFraction].
*
* @param colorTransitionFraction a `0.0` to `1.0` value that represents a color transition
- * percentage
+ * percentage
*/
@Stable
fun containerColor(colorTransitionFraction: Float): Color {
@@ -233,29 +236,35 @@
colors: TopAppBarColors,
) {
// Wrap the given actions in a Row.
- val actionsRow = @Composable {
- Row(
- horizontalArrangement = Arrangement.End,
- verticalAlignment = Alignment.CenterVertically,
- content = actions
- )
- }
+ val actionsRow =
+ @Composable {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ content = actions
+ )
+ }
// Compose a Surface with a TopAppBarLayout content.
- Surface(color = colors.scrolledContainerColor) {
+ Box(
+ modifier =
+ Modifier.drawBehind { drawRect(color = colors.scrolledContainerColor) }
+ .semantics { isTraversalGroup = true }
+ .pointerInput(Unit) {}
+ ) {
val height = LocalDensity.current.run { ContainerHeight.toPx() }
TopAppBarLayout(
- modifier = Modifier
- .windowInsetsPadding(windowInsets)
- // clip after padding so we don't show the title over the inset area
- .clipToBounds(),
+ modifier =
+ Modifier.windowInsetsPadding(windowInsets)
+ // clip after padding so we don't show the title over the inset area
+ .clipToBounds(),
heightPx = height,
navigationIconContentColor = colors.navigationIconContentColor,
titleContentColor = colors.titleContentColor,
actionIconContentColor = colors.actionIconContentColor,
title = title,
titleTextStyle = titleTextStyle,
- titleAlpha = 1f,
+ titleAlpha = { 1f },
titleVerticalArrangement = Arrangement.Center,
titleBottomPadding = 0,
hideTitleSemantics = false,
@@ -271,7 +280,7 @@
* composables.
*
* @throws [IllegalArgumentException] if the given [MaxHeightWithoutTitle] is equal or smaller than
- * the [pinnedHeight]
+ * the [pinnedHeight]
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -308,62 +317,67 @@
// Sets the app bar's height offset limit to hide just the bottom title area and keep top title
// visible when collapsed.
- SideEffect {
- if (scrollBehavior?.state?.heightOffsetLimit != pinnedHeightPx - maxHeightPx.floatValue) {
- scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx.floatValue
- }
- }
+ scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx.floatValue
// Obtain the container Color from the TopAppBarColors using the `collapsedFraction`, as the
// bottom part of this TwoRowsTopAppBar changes color at the same rate the app bar expands or
// collapse.
// This will potentially animate or interpolate a transition between the container color and the
// container's scrolled color according to the app bar's scroll state.
- val colorTransitionFraction = scrollBehavior?.state?.collapsedFraction ?: 0f
- val appBarContainerColor = colors.containerColor(colorTransitionFraction)
+ val colorTransitionFraction = { scrollBehavior?.state?.collapsedFraction ?: 0f }
+ val appBarContainerColor = { colors.containerColor(colorTransitionFraction()) }
// Wrap the given actions in a Row.
- val actionsRow = @Composable {
- Row(
- horizontalArrangement = Arrangement.End,
- verticalAlignment = Alignment.CenterVertically,
- content = actions
- )
- }
- val topTitleAlpha = TopTitleAlphaEasing.transform(colorTransitionFraction)
- val bottomTitleAlpha = 1f - colorTransitionFraction
+ val actionsRow =
+ @Composable {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ content = actions
+ )
+ }
+ val topTitleAlpha = { TopTitleAlphaEasing.transform(colorTransitionFraction()) }
+ val bottomTitleAlpha = { 1f - colorTransitionFraction() }
// Hide the top row title semantics when its alpha value goes below 0.5 threshold.
// Hide the bottom row title semantics when the top title semantics are active.
- val hideTopRowSemantics = colorTransitionFraction < 0.5f
+ val hideTopRowSemantics by
+ remember(colorTransitionFraction) { derivedStateOf { colorTransitionFraction() < 0.5f } }
val hideBottomRowSemantics = !hideTopRowSemantics
// Set up support for resizing the top app bar when vertically dragging the bar itself.
- val appBarDragModifier = if (scrollBehavior != null && !scrollBehavior.isPinned) {
- Modifier.draggable(
- orientation = Orientation.Vertical,
- state = rememberDraggableState { delta ->
- scrollBehavior.state.heightOffset += delta
- },
- onDragStopped = { velocity ->
- settleAppBar(
- scrollBehavior.state,
- velocity,
- scrollBehavior.flingAnimationSpec,
- scrollBehavior.snapAnimationSpec
- )
- }
- )
- } else {
- Modifier
- }
+ val appBarDragModifier =
+ if (scrollBehavior != null && !scrollBehavior.isPinned) {
+ Modifier.draggable(
+ orientation = Orientation.Vertical,
+ state =
+ rememberDraggableState { delta -> scrollBehavior.state.heightOffset += delta },
+ onDragStopped = { velocity ->
+ settleAppBar(
+ scrollBehavior.state,
+ velocity,
+ scrollBehavior.flingAnimationSpec,
+ scrollBehavior.snapAnimationSpec
+ )
+ }
+ )
+ } else {
+ Modifier
+ }
- Surface(modifier = modifier.then(appBarDragModifier), color = appBarContainerColor) {
+ Box(
+ modifier =
+ modifier
+ .then(appBarDragModifier)
+ .drawBehind { drawRect(color = appBarContainerColor()) }
+ .semantics { isTraversalGroup = true }
+ .pointerInput(Unit) {}
+ ) {
Column {
TopAppBarLayout(
- modifier = Modifier
- .windowInsetsPadding(windowInsets)
- // clip after padding so we don't show the title over the inset area
- .clipToBounds(),
+ modifier =
+ Modifier.windowInsetsPadding(windowInsets)
+ // clip after padding so we don't show the title over the inset area
+ .clipToBounds(),
heightPx = pinnedHeightPx,
navigationIconContentColor = colors.navigationIconContentColor,
titleContentColor = colors.titleContentColor,
@@ -378,27 +392,37 @@
actions = actionsRow,
)
TopAppBarLayout(
- modifier = Modifier
- // only apply the horizontal sides of the window insets padding, since the top
- // padding will always be applied by the layout above
- .windowInsetsPadding(windowInsets.only(WindowInsetsSides.Horizontal))
- .clipToBounds(),
- heightPx = maxHeightPx.floatValue - pinnedHeightPx +
- (scrollBehavior?.state?.heightOffset ?: 0f),
+ modifier =
+ Modifier
+ // only apply the horizontal sides of the window insets padding, since the
+ // top
+ // padding will always be applied by the layout above
+ .windowInsetsPadding(windowInsets.only(WindowInsetsSides.Horizontal))
+ .clipToBounds(),
+ heightPx =
+ maxHeightPx.floatValue - pinnedHeightPx +
+ (scrollBehavior?.state?.heightOffset ?: 0f),
navigationIconContentColor = colors.navigationIconContentColor,
titleContentColor = colors.titleContentColor,
actionIconContentColor = colors.actionIconContentColor,
title = {
- Box(modifier = Modifier.onGloballyPositioned { coordinates ->
- val measuredMaxHeightPx = density.run {
- MaxHeightWithoutTitle.toPx() + coordinates.size.height.toFloat()
- }
- // Allow larger max height for multi-line title, but do not reduce
- // max height to prevent flaky.
- if (measuredMaxHeightPx > defaultMaxHeightPx) {
- maxHeightPx.floatValue = measuredMaxHeightPx
- }
- }) { title() }
+ Box(
+ modifier =
+ Modifier.onGloballyPositioned { coordinates ->
+ val measuredMaxHeightPx =
+ density.run {
+ MaxHeightWithoutTitle.toPx() +
+ coordinates.size.height.toFloat()
+ }
+ // Allow larger max height for multi-line title, but do not reduce
+ // max height to prevent flaky.
+ if (measuredMaxHeightPx > defaultMaxHeightPx) {
+ maxHeightPx.floatValue = measuredMaxHeightPx
+ }
+ }
+ ) {
+ title()
+ }
},
titleTextStyle = titleTextStyle,
titleAlpha = bottomTitleAlpha,
@@ -420,21 +444,21 @@
* @param modifier a [Modifier]
* @param heightPx the total height this layout is capped to
* @param navigationIconContentColor the content color that will be applied via a
- * [LocalContentColor] when composing the navigation icon
+ * [LocalContentColor] when composing the navigation icon
* @param titleContentColor the color that will be applied via a [LocalContentColor] when composing
- * the title
+ * the title
* @param actionIconContentColor the content color that will be applied via a [LocalContentColor]
- * when composing the action icons
+ * when composing the action icons
* @param title the top app bar title (header)
* @param titleTextStyle the title's text style
* @param modifier a [Modifier]
* @param titleAlpha the title's alpha
* @param titleVerticalArrangement the title's vertical arrangement
* @param titleBottomPadding the title's bottom padding
- * @param hideTitleSemantics hides the title node from the semantic tree. Apply this
- * boolean when this layout is part of a [TwoRowsTopAppBar] to hide the title's semantics
- * from accessibility services. This is needed to avoid having multiple titles visible to
- * accessibility services at the same time, when animating between collapsed / expanded states.
+ * @param hideTitleSemantics hides the title node from the semantic tree. Apply this boolean when
+ * this layout is part of a [TwoRowsTopAppBar] to hide the title's semantics from accessibility
+ * services. This is needed to avoid having multiple titles visible to accessibility services at
+ * the same time, when animating between collapsed / expanded states.
* @param navigationIcon a navigation icon [Composable]
* @param actions actions [Composable]
* @param titleScaleDisabled whether the title font scaling is disabled. Default is disabled.
@@ -448,7 +472,7 @@
actionIconContentColor: Color,
title: @Composable () -> Unit,
titleTextStyle: TextStyle,
- titleAlpha: Float,
+ titleAlpha: () -> Float,
titleVerticalArrangement: Arrangement.Vertical,
titleBottomPadding: Int,
hideTitleSemantics: Boolean,
@@ -458,41 +482,33 @@
) {
Layout(
{
- Box(
- Modifier
- .layoutId("navigationIcon")
- .padding(start = TopAppBarHorizontalPadding)
- ) {
+ Box(Modifier.layoutId("navigationIcon").padding(start = TopAppBarHorizontalPadding)) {
CompositionLocalProvider(
LocalContentColor provides navigationIconContentColor,
content = navigationIcon
)
}
Box(
- Modifier
- .layoutId("title")
+ Modifier.layoutId("title")
.padding(horizontal = TopAppBarHorizontalPadding)
- .then(if (hideTitleSemantics) Modifier.clearAndSetSemantics { } else Modifier)
- .graphicsLayer(alpha = titleAlpha)
+ .then(if (hideTitleSemantics) Modifier.clearAndSetSemantics {} else Modifier)
+ .graphicsLayer { alpha = titleAlpha() }
) {
ProvideTextStyle(value = titleTextStyle) {
CompositionLocalProvider(
LocalContentColor provides titleContentColor,
- LocalDensity provides with(LocalDensity.current) {
- Density(
- density = density,
- fontScale = if (titleScaleDisabled) 1f else fontScale,
- )
- },
+ LocalDensity provides
+ with(LocalDensity.current) {
+ Density(
+ density = density,
+ fontScale = if (titleScaleDisabled) 1f else fontScale,
+ )
+ },
content = title
)
}
}
- Box(
- Modifier
- .layoutId("actionIcons")
- .padding(end = TopAppBarHorizontalPadding)
- ) {
+ Box(Modifier.layoutId("actionIcons").padding(end = TopAppBarHorizontalPadding)) {
CompositionLocalProvider(
LocalContentColor provides actionIconContentColor,
content = actions
@@ -502,20 +518,24 @@
modifier = modifier
) { measurables, constraints ->
val navigationIconPlaceable =
- measurables.first { it.layoutId == "navigationIcon" }
+ measurables
+ .first { it.layoutId == "navigationIcon" }
.measure(constraints.copy(minWidth = 0))
val actionIconsPlaceable =
- measurables.first { it.layoutId == "actionIcons" }
+ measurables
+ .first { it.layoutId == "actionIcons" }
.measure(constraints.copy(minWidth = 0))
- val maxTitleWidth = if (constraints.maxWidth == Constraints.Infinity) {
- constraints.maxWidth
- } else {
- (constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width)
- .coerceAtLeast(0)
- }
+ val maxTitleWidth =
+ if (constraints.maxWidth == Constraints.Infinity) {
+ constraints.maxWidth
+ } else {
+ (constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width)
+ .coerceAtLeast(0)
+ }
val titlePlaceable =
- measurables.first { it.layoutId == "title" }
+ measurables
+ .first { it.layoutId == "title" }
.measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth))
// Locate the title's baseline.
@@ -538,19 +558,23 @@
// Title
titlePlaceable.placeRelative(
x = max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width),
- y = when (titleVerticalArrangement) {
- Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2
- // Apply bottom padding from the title's baseline only when the Arrangement is
- // "Bottom".
- Arrangement.Bottom ->
- if (titleBottomPadding == 0) layoutHeight - titlePlaceable.height
- else layoutHeight - titlePlaceable.height - max(
- 0,
- titleBottomPadding - titlePlaceable.height + titleBaseline
- )
- // Arrangement.Top
- else -> 0
- }
+ y =
+ when (titleVerticalArrangement) {
+ Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2
+ // Apply bottom padding from the title's baseline only when the Arrangement
+ // is "Bottom".
+ Arrangement.Bottom ->
+ if (titleBottomPadding == 0) layoutHeight - titlePlaceable.height
+ else
+ layoutHeight -
+ titlePlaceable.height -
+ max(
+ 0,
+ titleBottomPadding - titlePlaceable.height + titleBaseline
+ )
+ // Arrangement.Top
+ else -> 0
+ }
)
// Action icons
@@ -562,7 +586,6 @@
}
}
-
/**
* Settles the app bar by flinging, in case the given velocity is greater than zero, and snapping
* after the fling settles.
@@ -587,9 +610,9 @@
if (flingAnimationSpec != null && abs(velocity) > 1f) {
var lastValue = 0f
AnimationState(
- initialValue = 0f,
- initialVelocity = velocity,
- )
+ initialValue = 0f,
+ initialVelocity = velocity,
+ )
.animateDecay(flingAnimationSpec) {
val delta = value - lastValue
val initialHeightOffset = state.heightOffset
@@ -603,9 +626,7 @@
}
// Snap if animation specs were provided.
if (snapAnimationSpec != null) {
- if (state.heightOffset < 0 &&
- state.heightOffset > state.heightOffsetLimit
- ) {
+ if (state.heightOffset < 0 && state.heightOffset > state.heightOffsetLimit) {
AnimationState(initialValue = state.heightOffset).animateTo(
if (state.collapsedFraction < 0.5f) {
0f
@@ -613,7 +634,9 @@
state.heightOffsetLimit
},
animationSpec = snapAnimationSpec
- ) { state.heightOffset = value }
+ ) {
+ state.heightOffset = value
+ }
}
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
index ea69eab..0a4f0d9 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
@@ -37,15 +37,11 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.test.assertHeightIsEqualTo
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo
-import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
import androidx.compose.ui.test.assertWidthIsEqualTo
-import androidx.compose.ui.test.getUnclippedBoundsInRoot
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
@@ -53,33 +49,24 @@
import androidx.compose.ui.test.swipeLeft
import androidx.compose.ui.test.swipeRight
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.height
-import androidx.compose.ui.unit.width
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.rootWidth
import com.android.settingslib.spa.testutils.setContentForSizeAssertions
import com.google.common.truth.Truth.assertThat
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@OptIn(ExperimentalMaterial3Api::class)
-@Ignore("b/346785755")
@RunWith(AndroidJUnit4::class)
class CustomizedAppBarTest {
- @get:Rule
- val rule = createComposeRule()
+ @get:Rule val rule = createComposeRule()
@Test
fun smallTopAppBar_expandsToScreen() {
rule
- .setContentForSizeAssertions {
- CustomizedTopAppBar(title = { Text("Title") })
- }
+ .setContentForSizeAssertions { CustomizedTopAppBar(title = { Text("Title") }) }
.assertHeightIsEqualTo(ContainerHeight)
.assertWidthIsEqualTo(rule.rootWidth())
}
@@ -88,51 +75,12 @@
fun smallTopAppBar_withTitle() {
val title = "Title"
rule.setContent {
- Box(Modifier.testTag(TopAppBarTestTag)) {
- CustomizedTopAppBar(title = { Text(title) })
- }
+ Box(Modifier.testTag(TopAppBarTestTag)) { CustomizedTopAppBar(title = { Text(title) }) }
}
rule.onNodeWithText(title).assertIsDisplayed()
}
@Test
- fun smallTopAppBar_default_positioning() {
- rule.setContent {
- Box(Modifier.testTag(TopAppBarTestTag)) {
- CustomizedTopAppBar(
- navigationIcon = {
- FakeIcon(Modifier.testTag(NavigationIconTestTag))
- },
- title = {
- Text("Title", Modifier.testTag(TitleTestTag))
- },
- actions = {
- FakeIcon(Modifier.testTag(ActionsTestTag))
- }
- )
- }
- }
- assertSmallDefaultPositioning()
- }
-
- @Test
- fun smallTopAppBar_noNavigationIcon_positioning() {
- rule.setContent {
- Box(Modifier.testTag(TopAppBarTestTag)) {
- CustomizedTopAppBar(
- title = {
- Text("Title", Modifier.testTag(TitleTestTag))
- },
- actions = {
- FakeIcon(Modifier.testTag(ActionsTestTag))
- }
- )
- }
- }
- assertSmallPositioningWithoutNavigation()
- }
-
- @Test
fun smallTopAppBar_titleDefaultStyle() {
var textStyle: TextStyle? = null
var expectedTextStyle: TextStyle? = null
@@ -188,29 +136,6 @@
assertThat(actionsColor).isEqualTo(expectedActionsColor)
}
- @Test
- fun largeTopAppBar_scrolled_positioning() {
- val content = @Composable { scrollBehavior: TopAppBarScrollBehavior? ->
- Box(Modifier.testTag(TopAppBarTestTag)) {
- CustomizedLargeTopAppBar(
- navigationIcon = {
- FakeIcon(Modifier.testTag(NavigationIconTestTag))
- },
- title = "Title",
- actions = {
- FakeIcon(Modifier.testTag(ActionsTestTag))
- },
- scrollBehavior = scrollBehavior,
- )
- }
- }
- assertLargeScrolledHeight(
- MaxHeightWithoutTitle + DefaultTitleHeight,
- MaxHeightWithoutTitle + DefaultTitleHeight,
- content,
- )
- }
-
@OptIn(ExperimentalMaterial3Api::class)
@Test
fun topAppBar_enterAlways_allowHorizontalScroll() {
@@ -221,14 +146,10 @@
}
rule.onNodeWithTag(LazyListTag).performTouchInput { swipeLeft() }
- rule.runOnIdle {
- assertThat(state.firstVisibleItemIndex).isEqualTo(1)
- }
+ rule.runOnIdle { assertThat(state.firstVisibleItemIndex).isEqualTo(1) }
rule.onNodeWithTag(LazyListTag).performTouchInput { swipeRight() }
- rule.runOnIdle {
- assertThat(state.firstVisibleItemIndex).isEqualTo(0)
- }
+ rule.runOnIdle { assertThat(state.firstVisibleItemIndex).isEqualTo(0) }
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -241,14 +162,10 @@
}
rule.onNodeWithTag(LazyListTag).performTouchInput { swipeLeft() }
- rule.runOnIdle {
- assertThat(state.firstVisibleItemIndex).isEqualTo(1)
- }
+ rule.runOnIdle { assertThat(state.firstVisibleItemIndex).isEqualTo(1) }
rule.onNodeWithTag(LazyListTag).performTouchInput { swipeRight() }
- rule.runOnIdle {
- assertThat(state.firstVisibleItemIndex).isEqualTo(0)
- }
+ rule.runOnIdle { assertThat(state.firstVisibleItemIndex).isEqualTo(0) }
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -257,21 +174,14 @@
lateinit var state: LazyListState
rule.setContent {
state = rememberLazyListState()
- MultiPageContent(
- TopAppBarDefaults.pinnedScrollBehavior(),
- state
- )
+ MultiPageContent(TopAppBarDefaults.pinnedScrollBehavior(), state)
}
rule.onNodeWithTag(LazyListTag).performTouchInput { swipeLeft() }
- rule.runOnIdle {
- assertThat(state.firstVisibleItemIndex).isEqualTo(1)
- }
+ rule.runOnIdle { assertThat(state.firstVisibleItemIndex).isEqualTo(1) }
rule.onNodeWithTag(LazyListTag).performTouchInput { swipeRight() }
- rule.runOnIdle {
- assertThat(state.firstVisibleItemIndex).isEqualTo(0)
- }
+ rule.runOnIdle { assertThat(state.firstVisibleItemIndex).isEqualTo(0) }
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -285,11 +195,7 @@
)
}
) { contentPadding ->
- LazyRow(
- Modifier
- .fillMaxSize()
- .testTag(LazyListTag), state
- ) {
+ LazyRow(Modifier.fillMaxSize().testTag(LazyListTag), state) {
items(2) { page ->
LazyColumn(
modifier = Modifier.fillParentMaxSize(),
@@ -308,146 +214,19 @@
}
/**
- * Checks the app bar's components positioning when it's a [CustomizedTopAppBar]
- * or a larger app bar that is scrolled up and collapsed into a small
- * configuration and there is no navigation icon.
- */
- private fun assertSmallPositioningWithoutNavigation(isCenteredTitle: Boolean = false) {
- val appBarBounds = rule.onNodeWithTag(TopAppBarTestTag).getUnclippedBoundsInRoot()
- val titleBounds = rule.onNodeWithTag(TitleTestTag).getUnclippedBoundsInRoot()
-
- val titleNode = rule.onNodeWithTag(TitleTestTag)
- // Title should be vertically centered
- titleNode.assertTopPositionInRootIsEqualTo((appBarBounds.height - titleBounds.height) / 2)
- if (isCenteredTitle) {
- // Title should be horizontally centered
- titleNode.assertLeftPositionInRootIsEqualTo(
- (appBarBounds.width - titleBounds.width) / 2
- )
- } else {
- // Title should now be placed 16.dp from the start, as there is no navigation icon
- // 4.dp padding for the whole app bar + 12.dp inset
- titleNode.assertLeftPositionInRootIsEqualTo(4.dp + 12.dp)
- }
-
- rule.onNodeWithTag(ActionsTestTag)
- // Action should still be placed at the end
- .assertLeftPositionInRootIsEqualTo(expectedActionPosition(appBarBounds.width))
- }
-
- /**
- * Checks the app bar's components positioning when it's a [CustomizedTopAppBar].
- */
- private fun assertSmallDefaultPositioning(isCenteredTitle: Boolean = false) {
- val appBarBounds = rule.onNodeWithTag(TopAppBarTestTag).getUnclippedBoundsInRoot()
- val titleBounds = rule.onNodeWithTag(TitleTestTag).getUnclippedBoundsInRoot()
- val appBarBottomEdgeY = appBarBounds.top + appBarBounds.height
-
- rule.onNodeWithTag(NavigationIconTestTag)
- // Navigation icon should be 4.dp from the start
- .assertLeftPositionInRootIsEqualTo(AppBarStartAndEndPadding)
- // Navigation icon should be centered within the height of the app bar.
- .assertTopPositionInRootIsEqualTo(
- appBarBottomEdgeY - AppBarTopAndBottomPadding - FakeIconSize
- )
-
- val titleNode = rule.onNodeWithTag(TitleTestTag)
- // Title should be vertically centered
- titleNode.assertTopPositionInRootIsEqualTo((appBarBounds.height - titleBounds.height) / 2)
- if (isCenteredTitle) {
- // Title should be horizontally centered
- titleNode.assertLeftPositionInRootIsEqualTo(
- (appBarBounds.width - titleBounds.width) / 2
- )
- } else {
- // Title should be 56.dp from the start
- // 4.dp padding for the whole app bar + 48.dp icon size + 4.dp title padding.
- titleNode.assertLeftPositionInRootIsEqualTo(4.dp + FakeIconSize + 4.dp)
- }
-
- rule.onNodeWithTag(ActionsTestTag)
- // Action should be placed at the end
- .assertLeftPositionInRootIsEqualTo(expectedActionPosition(appBarBounds.width))
- // Action should be 8.dp from the top
- .assertTopPositionInRootIsEqualTo(
- appBarBottomEdgeY - AppBarTopAndBottomPadding - FakeIconSize
- )
- }
-
- /**
- * Checks that changing values at a [CustomizedLargeTopAppBar] scroll behavior
- * affects the height of the app bar.
- *
- * This check partially and fully collapses the app bar to test its height.
- *
- * @param appBarMaxHeight the max height of the app bar [content]
- * @param appBarMinHeight the min height of the app bar [content]
- * @param content a Composable that adds a CustomizedLargeTopAppBar
- */
- @OptIn(ExperimentalMaterial3Api::class)
- private fun assertLargeScrolledHeight(
- appBarMaxHeight: Dp,
- appBarMinHeight: Dp,
- content: @Composable (TopAppBarScrollBehavior?) -> Unit
- ) {
- val fullyCollapsedOffsetDp = appBarMaxHeight - appBarMinHeight
- val partiallyCollapsedOffsetDp = fullyCollapsedOffsetDp / 3
- var partiallyCollapsedHeightOffsetPx = 0f
- var fullyCollapsedHeightOffsetPx = 0f
- lateinit var scrollBehavior: TopAppBarScrollBehavior
- rule.setContent {
- scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
- with(LocalDensity.current) {
- partiallyCollapsedHeightOffsetPx = partiallyCollapsedOffsetDp.toPx()
- fullyCollapsedHeightOffsetPx = fullyCollapsedOffsetDp.toPx()
- }
-
- content(scrollBehavior)
- }
-
- // Simulate a partially collapsed app bar.
- rule.runOnIdle {
- scrollBehavior.state.heightOffset = -partiallyCollapsedHeightOffsetPx
- scrollBehavior.state.contentOffset = -partiallyCollapsedHeightOffsetPx
- }
- rule.waitForIdle()
- rule.onNodeWithTag(TopAppBarTestTag)
- .assertHeightIsEqualTo(
- appBarMaxHeight - partiallyCollapsedOffsetDp
- )
-
- // Simulate a fully collapsed app bar.
- rule.runOnIdle {
- scrollBehavior.state.heightOffset = -fullyCollapsedHeightOffsetPx
- // Simulate additional content scroll beyond the max offset scroll.
- scrollBehavior.state.contentOffset =
- -fullyCollapsedHeightOffsetPx - partiallyCollapsedHeightOffsetPx
- }
- rule.waitForIdle()
- // Check that the app bar collapsed to its min height.
- rule.onNodeWithTag(TopAppBarTestTag).assertHeightIsEqualTo(appBarMinHeight)
- }
-
- /**
* An [IconButton] with an [Icon] inside for testing positions.
*
* An [IconButton] is defaulted to be 48X48dp, while its child [Icon] is defaulted to 24x24dp.
*/
- private val FakeIcon = @Composable { modifier: Modifier ->
- IconButton(
- onClick = { /* doSomething() */ },
- modifier = modifier.semantics(mergeDescendants = true) {}
- ) {
- Icon(ColorPainter(Color.Red), null)
+ private val FakeIcon =
+ @Composable { modifier: Modifier ->
+ IconButton(
+ onClick = { /* doSomething() */ },
+ modifier = modifier.semantics(mergeDescendants = true) {}
+ ) {
+ Icon(ColorPainter(Color.Red), null)
+ }
}
- }
-
- private fun expectedActionPosition(appBarWidth: Dp): Dp =
- appBarWidth - AppBarStartAndEndPadding - FakeIconSize
-
- private val FakeIconSize = 48.dp
- private val AppBarStartAndEndPadding = 4.dp
- private val AppBarTopAndBottomPadding = (ContainerHeight - FakeIconSize) / 2
private val LazyListTag = "lazyList"
private val TopAppBarTestTag = "bar"
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/ComposeContentTestRuleExt.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/ComposeContentTestRuleExt.kt
index 0436fc2..53d4531 100644
--- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/ComposeContentTestRuleExt.kt
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/ComposeContentTestRuleExt.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spa.testutils
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
@@ -75,7 +76,7 @@
setContent {
SettingsTheme {
Surface {
- Box {
+ Box(Modifier.safeDrawingPadding()) {
Box(
Modifier
.sizeIn(maxWidth = parentMaxWidth, maxHeight = parentMaxHeight)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBoolean.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBoolean.kt
index 5d67f57..42528695 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBoolean.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBoolean.kt
@@ -21,16 +21,25 @@
import android.provider.Settings
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-fun Context.settingsGlobalBoolean(name: String, defaultValue: Boolean = false):
- ReadWriteProperty<Any?, Boolean> = SettingsGlobalBooleanDelegate(this, name, defaultValue)
+fun Context.settingsGlobalBoolean(
+ name: String,
+ defaultValue: Boolean = false,
+): ReadWriteProperty<Any?, Boolean> = SettingsGlobalBooleanDelegate(this, name, defaultValue)
fun Context.settingsGlobalBooleanFlow(name: String, defaultValue: Boolean = false): Flow<Boolean> {
val value by settingsGlobalBoolean(name, defaultValue)
- return settingsGlobalChangeFlow(name).map { value }.distinctUntilChanged()
+ return settingsGlobalChangeFlow(name)
+ .map { value }
+ .distinctUntilChanged()
+ .conflate()
+ .flowOn(Dispatchers.Default)
}
private class SettingsGlobalBooleanDelegate(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureBoolean.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureBoolean.kt
index 25090a4..d5f4ec2 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureBoolean.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureBoolean.kt
@@ -22,16 +22,25 @@
import com.android.settingslib.spaprivileged.database.contentChangeFlow
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-fun Context.settingsSecureBoolean(name: String, defaultValue: Boolean = false):
- ReadWriteProperty<Any?, Boolean> = SettingsSecureBooleanDelegate(this, name, defaultValue)
+fun Context.settingsSecureBoolean(
+ name: String,
+ defaultValue: Boolean = false,
+): ReadWriteProperty<Any?, Boolean> = SettingsSecureBooleanDelegate(this, name, defaultValue)
fun Context.settingsSecureBooleanFlow(name: String, defaultValue: Boolean = false): Flow<Boolean> {
val value by settingsSecureBoolean(name, defaultValue)
- return contentChangeFlow(Settings.Secure.getUriFor(name)).map { value }.distinctUntilChanged()
+ return contentChangeFlow(Settings.Secure.getUriFor(name))
+ .map { value }
+ .distinctUntilChanged()
+ .conflate()
+ .flowOn(Dispatchers.Default)
}
private class SettingsSecureBooleanDelegate(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureString.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureString.kt
new file mode 100644
index 0000000..a706308
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureString.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.settingslib.spaprivileged.settingsprovider
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings
+import com.android.settingslib.spaprivileged.database.contentChangeFlow
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+fun Context.settingsSecureString(
+ name: String,
+ defaultValue: String = ""
+): ReadWriteProperty<Any?, String> = SettingsSecureStringDelegate(this, name, defaultValue)
+
+fun Context.settingsSecureStringFlow(name: String, defaultValue: String = ""): Flow<String> {
+ val value by settingsSecureString(name, defaultValue)
+ return contentChangeFlow(Settings.Secure.getUriFor(name))
+ .map { value }
+ .distinctUntilChanged()
+ .conflate()
+ .flowOn(Dispatchers.Default)
+}
+
+private class SettingsSecureStringDelegate(
+ context: Context,
+ private val name: String,
+ private val defaultValue: String = "",
+) : ReadWriteProperty<Any?, String> {
+
+ private val contentResolver: ContentResolver = context.contentResolver
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): String =
+ Settings.Secure.getString(contentResolver, name) ?: defaultValue
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
+ Settings.Secure.putString(contentResolver, name, value)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index 2a04424..627b248 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Process
+import android.os.UserHandle
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
@@ -88,7 +89,8 @@
*
* If true, the app gets all permissions, so the permission toggle always not changeable.
*/
-fun AppRecord.isSystemOrRootUid(): Boolean = app.uid in listOf(Process.SYSTEM_UID, Process.ROOT_UID)
+fun AppRecord.isSystemOrRootUid(): Boolean =
+ UserHandle.getAppId(app.uid) in listOf(Process.SYSTEM_UID, Process.ROOT_UID)
/**
* Gets whether the permission on / off is changeable for the given app.
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureStringTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureStringTest.kt
new file mode 100644
index 0000000..e3d182b
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsSecureStringTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.settingslib.spaprivileged.settingsprovider
+
+import android.content.Context
+import android.provider.Settings
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsSecureStringTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun getValue_returnCorrectValue() {
+ Settings.Secure.putString(context.contentResolver, TEST_NAME, VALUE)
+
+ val value by context.settingsSecureString(TEST_NAME)
+
+ assertThat(value).isEqualTo(VALUE)
+ }
+
+ @Test
+ fun setValue_correctValueSet() {
+ var value by context.settingsSecureString(TEST_NAME)
+
+ value = VALUE
+
+ assertThat(Settings.Secure.getString(context.contentResolver, TEST_NAME)).isEqualTo(VALUE)
+ }
+
+ @Test
+ fun settingsSecureStringFlow_valueNotChanged() = runBlocking {
+ var value by context.settingsSecureString(TEST_NAME)
+ value = VALUE
+
+ val flow = context.settingsSecureStringFlow(TEST_NAME)
+
+ assertThat(flow.firstWithTimeoutOrNull()).isEqualTo(VALUE)
+ }
+
+ @Test
+ fun settingsSecureStringFlow_collectAfterValueChanged_onlyKeepLatest() = runBlocking {
+ var value by context.settingsSecureString(TEST_NAME)
+ value = ""
+
+ val flow = context.settingsSecureStringFlow(TEST_NAME)
+ value = VALUE
+
+ assertThat(flow.firstWithTimeoutOrNull()).isEqualTo(VALUE)
+ }
+
+ @Test
+ fun settingsSecureStringFlow_collectBeforeValueChanged_getBoth() = runBlocking {
+ var value by context.settingsSecureString(TEST_NAME)
+ value = ""
+
+ val listDeferred = async { context.settingsSecureStringFlow(TEST_NAME).toListWithTimeout() }
+ delay(100)
+ value = VALUE
+
+ assertThat(listDeferred.await()).containsAtLeast("", VALUE).inOrder()
+ }
+
+ private companion object {
+ const val TEST_NAME = "test_string_delegate"
+ const val VALUE = "value"
+ }
+}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 32557b9..4ac3e67 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -79,3 +79,23 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_determining_spatial_audio_attributes_by_profile"
+ namespace: "cross_device_experiences"
+ description: "Use bluetooth profile connection policy to determine spatial audio attributes"
+ bug: "341005211"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "volume_panel_broadcast_fix"
+ namespace: "systemui"
+ description: "Make the volume panel's repository listen for the new ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED broadcast instead of ACTION_NOTIFICATION_POLICY_CHANGED"
+ bug: "347707024"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/drawable/ic_do_not_disturb_on_24dp.xml b/packages/SettingsLib/res/drawable/ic_do_not_disturb_on_24dp.xml
new file mode 100644
index 0000000..06c0d8c
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_do_not_disturb_on_24dp.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,16.41 16.41,20 12,20z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M7,11h10v2h-10z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 6b6f803..2db6f62 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Laat laai van GPU-ontfoutlae vir ontfoutapps toe"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Aktiveer woordryke verkoperloginskrywing"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Sluit bykomende toestelspesifieke verkoperloglêers by foutverslae in, wat privaat inligting kan bevat, meer batterykrag kan gebruik, en/of meer berging kan gebruik."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Vensteranimasieskaal"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Oorganganimasieskaal"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator-tydsduurskaal"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en onthounotas"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Laat hierdie app toe om wekkers te stel en tydsensitiewe handelinge te skeduleer. Dit laat die app op die agtergrond werk, wat meer batterykrag kan gebruik.\n\nAs hierdie toestemming af is, sal bestaande wekkers en tydgegronde geleenthede wat deur hierdie app geskeduleer is, nie werk nie."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"skedule, wekker, onthounota, horlosie"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Moenie Steur Nie"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Skakel aan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Skakel Moenie steur nie aan"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nooit"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Tydsduur"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vra elke keer"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Totdat jy dit afskakel"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Geen naam nie)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Hierdie foon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Hierdie tablet"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 6f494f0..732ec6f 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ለስህተት ማረሚያ መተግበሪያዎች የጂፒዩ ንብርብሮችን መስቀልን ፍቀድ"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"የዝርክርክ ቃላት አቅራቢ ምዝግብ ማስታወሻን መያዝ አንቃ"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"በሳንካ ሪፖርቶች ውስጥ ተጨማሪ መሣሪያ-ተኮር የአቅራቢ ምዝግብ ማስታወሻዎችን ያካትቱ፣ ይህም የግል መረጃን ሊይዝ፣ ተጨማሪ ባትሪ ሊፈጅ እና/ወይም ተጨማሪ ማከማቻ ሊጠቀም ይችላል።"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"የ Window እነማ ልኬት ለውጥ"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"የእነማ ልኬት ለውጥ ሽግግር"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"እነማ አድራጊ ቆይታ መለኪያ"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ማንቂያዎች እና አስታዋሾች"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ይህ መተግበሪያ ማንቂያዎችን እንዲያቀናብር እና የጊዜ ትብነት ያላቸው እርምጃዎችን መርሐግብር እንዲያስይዝ ይፍቀዱለት። ይህ መተግበሪያው ከበስተጀርባ ማሄድ እንዲችል ያስችለዋል፣ ይህም የበለጠ ባትሪ ሊጠቀም ይችላል።\n\nይህ ፈቃድ ከጠፋ በዚህ መተግበሪያ መርሐግብር የተያዘላቸው ነባር ማንቂያዎች እና ጊዜ-ተኮር ክስተቶች አይሰሩም።"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"የጊዜ መርሐግብር፣ ማንቂያ፣ አስታዋሽ ሰዓት"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"አይረብሹ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"አብራ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"አትረብሽን አብራ"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"በጭራሽ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"የቆይታ ጊዜ"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ሁልጊዜ ጠይቅ"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"እስኪያጠፉት ድረስ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(ስም የለም)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ይህ ስልክ"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ይህ ጡባዊ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 4b11a76..2964930 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"السماح بتحميل طبقات تصحيح أخطاء GPU لتطبيقات تصحيح الأخطاء"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"تفعيل التسجيل المطوَّل للمورّد"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"تضمين سجلات المورّدين الإضافية الخاصة بالجهاز في تقارير الخطأ، والتي قد تحتوي على معلومات شخصية و/أو تستهلك المزيد من شحن البطارية و/أو تستهلك المزيد من مساحة التخزين"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"حجم الرسوم المتحركة للنافذة"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"حجم الرسوم المتحركة للنقل"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"طول مدة الرسوم المتحركة"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"المنبّهات والتذكيرات"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"يمكنك السماح لهذا التطبيق بضبط المنبّهات وجدولة الإجراءات لتنفيذها في الوقت المناسب. ويسمح هذا الإذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من البطارية.\n\nفي حال عدم تفعيل هذا الإذن، لن تعمل المنبهات المضبوطة والأحداث المستندة إلى الوقت المجدولة حاليًا في هذا التطبيق."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"جدول زمني، جدولة، منبّه، تذكير، ساعة"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"وضع \"عدم الإزعاج\""</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"تفعيل"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"تفعيل ميزة \"عدم الإزعاج\""</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"مطلقًا"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"المدة"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"السؤال في كل مرة"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(بلا اسم)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"هذا الهاتف"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"هذا الجهاز اللوحي"</string>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index 5a06cb3..b5dfde5 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -71,7 +71,7 @@
<string-array name="bt_hci_snoop_log_profile_filter_entries">
<item msgid="3961868665260627524">"অক্ষম কৰক"</item>
<item msgid="2505973306504851132">"বৰ্ণৰ ষ্ট্ৰীঙেৰে পূৰ কৰক"</item>
- <item msgid="5883011000629613855">"কেৱল হেডাৰ এৰক"</item>
+ <item msgid="5883011000629613855">"কেৱল হে’ডাৰ এৰক"</item>
<item msgid="1051534112762023603">"সম্পূৰ্ণকৈ আঁতৰাওক"</item>
</string-array>
<string-array name="bluetooth_avrcp_versions">
@@ -276,8 +276,8 @@
</string-array>
<string-array name="usb_configuration_titles">
<item msgid="3358668781763928157">"চাৰ্জ কৰি থকা হৈছে"</item>
- <item msgid="7804797564616858506">"এমটিপি (মিডিয়া ট্ৰান্সফাৰ প্ৰ’ট’কল)"</item>
- <item msgid="910925519184248772">"পিটিপি (পিকচাৰ ট্ৰান্সফাৰ প্ৰ’ট’কল)"</item>
+ <item msgid="7804797564616858506">"এমটিপি (মিডিয়া ট্ৰান্সফাৰ প্ৰ্ৰ\'টকল)"</item>
+ <item msgid="910925519184248772">"পিটিপি (পিকচাৰ ট্ৰান্সফাৰ প্ৰ্ৰ\'টকল)"</item>
<item msgid="3825132913289380004">"RNDIS (USB ইথাৰনেট)"</item>
<item msgid="8828567335701536560">"ধ্বনিৰ উৎস"</item>
<item msgid="8688681727755534982">"এমআইডিআই"</item>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index d021647..e73462e 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -132,8 +132,8 @@
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইণ্টাৰনেট সংযোগ শ্বেয়াৰ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"পাঠ বাৰ্তা"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিমৰ এক্সেছ"</string>
- <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
- <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"এইচ্ছডি অডিঅ’"</string>
+ <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
+ <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD অডিঅ’"</string>
<string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"শ্ৰৱণ যন্ত্ৰ"</string>
<string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE অডিঅ’"</string>
<string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"শ্ৰৱণ যন্ত্ৰৰ সৈতে সংযোগ কৰা হৈছে"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ডিবাগ এপসমূহৰ বাবে জিপিইউ ডিবাগ তৰপ ল\'ড কৰিবলৈ অনুমতি দিয়ক"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"বিক্ৰেতাৰ ভাৰ্ব’ছ লগিং সক্ষম কৰক"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"বাগ ৰিপ’ৰ্টসমূহত অতিৰিক্ত ডিভাইচ নিৰ্দিষ্ট বিক্ৰেতাৰ লগসমূহ অন্তৰ্ভুক্ত কৰক, য’ত ব্যক্তিগত তথ্য থাকিব পাৰে, যি অধিক বেটাৰী আৰু/অথবা ষ্ট’ৰেজ ব্যৱহাৰ কৰিব পাৰে।"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"ৱিণ্ড\' এনিমেশ্বন স্কেল"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ট্ৰাঞ্জিশ্বন এনিমেশ্বন স্কেল"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"এনিমেটৰ কালদৈৰ্ঘ্য স্কেল"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"এই এপ্টোক এলাৰ্ম ছেট কৰিবলৈ আৰু সময় সংবেদনশীল কাৰ্যৰ সময়সূচী নিৰ্ধাৰণ কৰিবলৈ দিয়ক। ই এপ্টোক নেপথ্যত চলি থকাৰ অনুমতি দিয়ে যাৰ ফলত অধিক বেটাৰী ব্যৱহাৰ হয়।\n\nএই অনুমতিটো অফ কৰা থাকিলে, ইতিমধ্যে ছেট কৰা এলাৰ্ম আৰু এই এপ্টোৱে সময়সূচী নিৰ্ধাৰণ কৰা সময় ভিত্তিক অনুষ্ঠানসমূহে কাম নকৰা হ’ব।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"সময়সূচী, এলাৰ্ম, ৰিমাইণ্ডাৰ, ঘড়ী"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"অসুবিধা নিদিব"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"অন কৰক"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"অসুবিধা নিদিব অন কৰক"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"কেতিয়াও নহয়"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"সময়সীমা"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্ৰতিবাৰতে সোধক"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"আপুনি অফ নকৰা পর্যন্ত"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(কোনো নাম নাই)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফ’নটো"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই টেবলেটটো"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index b7dda83..2ae6cd3 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Qrafik prosessor qatları sazlanmasının yüklənməsinə icazə verilsin"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Təfsilatlı təchizatçı jurnalı"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Xəta hesabatına təchizatçının cihaz haqqında əlavə qeydləri daxil edilsin. Qeydlərdə şəxsi məlumatlar ola, onlar artıq yer tuta və enerji sərfiyyatını artıra bilər."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Pəncərə animasiyası"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Keçid animasiyası"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animasiya müddəti"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Siqnallar və xatırlatmalar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu tətbiqə siqnallar ayarlamağa və vaxta əsaslanan əməliyyatları planlaşdırmağa icazə verin. Bu, tətbiqin arxa fonda işləməsinə imkan verir ki, nəticədə daha çox enerji istifadə edilə bilər.\n\nBu icazə deaktiv olsa, bu tətbiq tərəfindən planlaşdırılan mövcud siqnallar və vaxta əsaslanan tədbirlər işləməyəcəkdir."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"cədvəl, siqnal, xatırlatma, saat"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Narahat etməyin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktiv edin"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Narahat Etməyin\" rejimini aktiv edin"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Heç vaxt"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Müddət"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Həmişə soruşulsun"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Deaktiv edilənə qədər"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Ad yoxdur)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"İndicə"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu planşet"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 0e42066..e1e93ef 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Učitava otklanjanje grešaka GPU-a u apl. za otklanjanje grešaka"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Opširne evidencije prodavca"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Uvrštava u izveštaje o greškama dodatne posebne evidencije prodavca za uređaje, koje mogu da sadrže privatne podatke, da troše više baterije i/ili da koriste više memorije."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Razmera animacije prozora"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Razmera animacije prelaza"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animatorova razmera trajanja"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsetnici"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Omogućite ovoj aplikaciji da podešava alarme i zakazuje vremenski osetljive radnje. To omogućava da aplikacija bude pokrenuta u pozadini, što može da troši više baterije.\n\nAko je ova dozvola isključena, postojeći alarmi i događaji zasnovani na vremenu zakazani pomoću ove aplikacije neće raditi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"zakazati, alarm, podsetnik, sat"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne uznemiravaj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključite režim Ne uznemiravaj"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nikad"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trajanje"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Bez imena)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 95586ea..00eb95d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Загружаць слаі адладкі GPU для праграм адладкі"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Уключыць падрабязны журнал пастаўшчыка"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Дадаваць у справаздачы пра памылкі дадатковыя журналы пастаўшчыка для пэўнай прылады (могуць утрымлівацца прыватныя даныя, можа павышацца выкарыстанне акумулятара і/ці памяці)."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Маштаб анімацыі акна"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Маштаб перадачы анімацыі"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Працягласць анімацыі"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будзільнікі і напаміны"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дазвольце гэтай праграме ўключаць будзільнікі і задаваць час дзеянняў. З такім дазволам праграма можа працаваць у фонавым рэжыме і ў выніку хутчэй разраджаць акумулятар.\n\nКалі вы не ўключыце гэты дазвол, існуючыя будзільнікі і запланаваны праграмай час падзей не будуць працаваць."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"расклад, будзільнік, напамін, гадзіннік"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не турбаваць"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Уключыць"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Уключэнне рэжыму \"Не турбаваць\""</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ніколі"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Працягласць"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Заўсёды пытацца"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Пакуль не выключыце"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Без назвы)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Гэты тэлефон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Гэты планшэт"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 54fef5f..a2db0f9 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Разреш. на зарежд. на слоевете за отстр. на грешки в ГП за съотв. прилож."</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Активиране на подробно регистр. на файлове за доставчиците"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Включване на допълнителни регистрационни файлове за доставчиците на конкретни устройства в сигналите за програмни грешки, които може да съдържат поверителна информация, да изразходват батерията в по-голяма степен и/или да използват повече място в хранилището."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Скала на прозореца на аним."</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Скала на преходната анимация"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Скала за Animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будилници и напомняния"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Разрешаване на това приложение да задава будилници и да насрочва действия, ограничени във времето. Това му позволява да работи на заден план, при което може да се използва повече батерия.\n\nАко разрешението е изключено, съществуващите будилници и събитията въз основа на времето, насрочени от приложението, няма да работят."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"график, будилник, напомняне, часовник"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не безпокойте"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включване"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включване на режима „Не безпокойте“"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Никога"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Времетраене"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Да се пита винаги"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"До изключване"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Няма име)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Този телефон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Този таблет"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 1533a19..f8118f4 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ডিবাগ অ্যাপের জন্য GPU ডিবাগ স্তর লোড হতে দিন"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"ভারবোস ভেন্ডর লগ-ইন চালু করুন"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"সমস্যা সংক্রান্ত রিপোর্টগুলিতে অতিরিক্ত ডিভাইস-নির্দিষ্ট ভেন্ডরের লগগুলি অন্তর্ভুক্ত করুন, যার মধ্যে ব্যক্তিগত তথ্য থাকতে পারে, আরও বেশি ব্যাটারি ব্যবহার করতে পারে, এবং/অথবা আরও স্টোরেজ ব্যবহার করতে পারে।"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"উইন্ডো অ্যানিমেশন স্কেল"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ট্র্যানজিশন অ্যানিমেশন স্কেল"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"অ্যানিমেটর সময়কাল স্কেল"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"অ্যালার্ম এবং রিমাইন্ডার"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"অ্যালার্ম এবং সময়ের মধ্যে শেষ করতে হবে এমন অ্যাকশনের শিডিউল সেট করতে এই অ্যাপকে অনুমতি দিন। এর ফলে ব্যাকগ্রাউন্ডে অ্যাপ চলতে পারে, যার জন্য আরও ব্যাটারির চার্জ খরচ হতে পারে।\n\nএই অনুমতি বন্ধ করা থাকলে, আগে থেকে থাকা অ্যালার্ম এবং অ্যাপের মাধ্যমে শিডিউল করা সময় ভিত্তিক ইভেন্টের রিমাইন্ডার কাজ করবে না।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"শিডিউল, অ্যালার্ম, রিমাইন্ডার, ঘড়ি"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"বিরক্ত করবে না"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"চালু করুন"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"কখনও নয়"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"সময়কাল"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্রতিবার জিজ্ঞেস করা হবে"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(কোনও নাম নেই)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফোন"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"এই ট্যাবলেট"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 15a9618..2288f05 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Omogućite slojeve za otkl. grešaka na GPU-u za apl. za otkl. grešaka"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Omogući opširni zapisnik"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"U izvještaje o greškama uključite dodatne zapisnike dobavljača specifične za uređaj, koji mogu sadržavati lične informacije, povećati potrošnju baterije i/ili koristiti više prostora za pohranu."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Skala animacije prozora"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Skala animacije prijelaza"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Skala trajanja animatora"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dozvolite ovoj aplikaciji da postavlja alarme i zakazuje vremenski osjetljive radnje. Ovim će se omogućiti aplikaciji da radi u pozadini, čime se može povećati potrošnja baterije.\n\nAko je ovo odobrenje isključeno, postojeći alarmi i događaji zasnovani na vremenu, a koje je ova aplikacija zakazala, neće funkcionirati."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne ometaj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključi način rada Ne ometaj"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nikada"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trajanje"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Bez imena)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 45f08da..61a6e82 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permet carregar capes de depuració de GPU en aplicacions de depuració"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Activa el registre detallat del proveïdor"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclou altres registres de proveïdor específics del dispositiu als informes d’errors; és possible que continguin informació privada, consumeixin més bateria o utilitzin més espai d\'emmagatzematge"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala d\'animació de la finestra"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala d\'animació de la transició"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de durada de l\'animació"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes i recordatoris"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permet que aquesta aplicació configuri alarmes i programi accions a una hora determinada. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programació, alarma, recordatori, rellotge"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"No molestis"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activa"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activa el mode No molestis"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Mai"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durada"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pregunta sempre"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sense nom)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Aquest telèfon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Aquesta tauleta"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 194c616..3444430 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Povolit načítání vrstev ladění GPU pro ladicí aplikace"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Povolit podrobné protokolování dodavatele"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Zahrnovat do zpráv o chybách dodatečné protokoly dodavatelů specifické pro zařízení, které mohou obsahovat soukromé údaje, více vybíjet baterii nebo využívat více místa v úložišti."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Měřítko animace okna"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Měřítko animace přeměny"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Měřítko délky animace"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Budíky a připomenutí"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Když tuto možnost povolíte, aplikace bude moci nastavovat budíky a plánovat akce závislé na čase. Aplikace poběží na pozadí, což může vést k vyšší spotřebě baterie.\n\nPokud toto oprávnění zůstane vypnuté, stávající budíky a události závislé na čase naplánované touto aplikací nebudou fungovat."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plán, budík, připomenutí, hodiny"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Nerušit"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Zapnout"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zapněte funkci Nerušit"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nikdy"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trvání"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pokaždé se zeptat"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokud funkci nevypnete"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Bez jména)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 9e47d6d..1bff907 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Tillad, at fejlretningslag indlæses for grafikprocessor i apps til fejlretning"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Aktivér detaljeret leverandørlogging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Medtag yderligere enhedsspecifikke leverandørlogfiler i fejlrapporter, som muligvis indeholder personlige oplysninger. Dette bruger muligvis mere batteri og/eller lagerplads."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Animationsskala for vindue"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Overgangsanimationsskala"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animatorvarighedsskala"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmer og påmindelser"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tillad, at denne app indstiller alarmer og planlægger tidsbestemte handlinger. Appen vil køre i baggrunden, hvor den muligvis bruger mere batteri.\n\nHvis denne tilladelse er deaktiveret, vil eksisterende alarmer og tidsbestemte handlinger, der er planlagt af denne app, ikke fungere."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planlæg, alarm, påmindelse, ur"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Forstyr ikke"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivér"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivér Forstyr ikke"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Aldrig"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Varighed"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spørg hver gang"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Indtil du deaktiverer"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Intet navn)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Denne tablet"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index caeee06..64139a9 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Debug-Apps das Laden von GPU-Debug-Ebenen erlauben"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ausführliche Protokollierung aktivieren"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Schließt zusätzliche gerätespezifische Anbieterprotokolle in Fehlerberichten ein, die private Informationen enthalten, den Akkuverbrauch erhöhen und/oder zusätzlichen Speicher benötigen können."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Fensteranimationsfaktor"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Übergangsanimationsfaktor"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animationsdauerfaktor"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wecker und Erinnerungen"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dieser App erlauben, Wecker zu stellen und zeitgebundene Aktionen zu planen. Dadurch läuft die App im Hintergrund. Dies kann den Akkuverbrauch erhöhen. \n\nWenn diese Berechtigung deaktiviert ist, funktionieren bereits gestellte Wecker und zeitgebundene Ereignisse, die von dieser App geplant sind, nicht wie erwartet."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planen, Wecker, Erinnerung, Uhr"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Bitte nicht stören"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivieren"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„Bitte nicht stören“ aktivieren"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nie"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Dauer"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Jedes Mal fragen"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Kein Name)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Dieses Smartphone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dieses Tablet"</string>
@@ -708,7 +722,7 @@
<string name="physical_keyboard_title" msgid="4811935435315835220">"Physische Tastatur"</string>
<string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Tastaturlayout wählen"</string>
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standard"</string>
- <string name="turn_screen_on_title" msgid="2662312432042116026">"Steuerelement zum Aktivieren des Displays"</string>
+ <string name="turn_screen_on_title" msgid="2662312432042116026">"Berechtigung zum Aktivieren des Displays"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"Aktivieren des Displays erlauben"</string>
<string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne dass du dies beabsichtigst."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 94d4297..f8a6944 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Φόρτωση επιπ. εντοπ. σφ. GPU για εφαρμ. αντιμ. σφ."</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ενεργ. λεπτ. καταγραφής προμ."</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Συμπερίληψη πρόσθετων αρχείων καταγραφής προμηθευτή για συγκεκριμένες συσκευές στις αναφορές σφαλμάτων, τα οποία ενδέχεται να περιέχουν ιδιωτικές πληροφορίες, να χρησιμοποιούν περισσότερη μπαταρία ή/και να χρησιμοποιούν περισσότερο αποθηκευτικό χώρο."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Κλίμακα κίνησης παραθύρου"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Κλίμακα κίνησης μετάβασης"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Κλίμ. διάρ. Animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ξυπνητήρια και υπενθυμίσεις"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Επιτρέψτε σε αυτή την εφαρμογή να ορίζει ξυπνητήρια και να προγραμματίζει ενέργειες που εξαρτώνται από τον χρόνο. Αυτό επιτρέπει στην εφαρμογή να εκτελείται στο παρασκήνιο και, ως εκ τούτου, μπορεί να καταναλώνει περισσότερη μπαταρία.\n\nΑν αυτή η άδεια δεν είναι ενεργή, τα υπάρχοντα ξυπνητήρια και συμβάντα βάσει χρόνου που έχουν προγραμματιστεί από αυτή την εφαρμογή δεν θα λειτουργούν."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"πρόγραμμα, ξυπνητήρι, υπενθύμιση, ρολόι"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Μην ενοχλείτε"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ενεργοποίηση"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ενεργοποίηση λειτουργίας \"Μην ενοχλείτε\""</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ποτέ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Διάρκεια"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Να ερωτώμαι κάθε φορά"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Μέχρι την απενεργοποίηση"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Χωρίς όνομα)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Μόλις τώρα"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Αυτό το tablet"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index c4d33e0..a6367f1 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Allow loading GPU debug layers for debug apps"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Enable verbose vendor logging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Include additional device-specific vendor logs in bug reports, which may contain private information, use more battery and/or use more storage."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Window animation scale"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Transition animation scale"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator duration scale"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Never"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duration"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(No name)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 42f9b5f..975cc5e 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -408,6 +408,12 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Allow loading GPU debug layers for debug apps"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Enable verbose vendor logging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Include additional device-specific vendor logs in bug reports, which may contain private information, use more battery, and/or use more storage."</string>
+ <string name="enable_verbose_vendor_logging_checkbox" msgid="3864578373293835530">"Disable after one day"</string>
+ <string name="verbose_vendor_logging_notification_title" msgid="6811217272559843592">"Verbose vendor logging has ended"</string>
+ <string name="verbose_vendor_logging_notification_summary" msgid="5226524769774370942">"Enabled for one day"</string>
+ <string name="verbose_vendor_logging_notification_action" msgid="1190831050259046071">"Enable for one more day"</string>
+ <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"Disables after one day"</string>
+ <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Enabled indefinitely"</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Window animation scale"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Transition animation scale"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator duration scale"</string>
@@ -557,6 +563,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms & reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Never"</string>
@@ -569,6 +576,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duration"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(No name)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index c4d33e0..a6367f1 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Allow loading GPU debug layers for debug apps"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Enable verbose vendor logging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Include additional device-specific vendor logs in bug reports, which may contain private information, use more battery and/or use more storage."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Window animation scale"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Transition animation scale"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator duration scale"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Never"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duration"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(No name)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index c4d33e0..a6367f1 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Allow loading GPU debug layers for debug apps"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Enable verbose vendor logging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Include additional device-specific vendor logs in bug reports, which may contain private information, use more battery and/or use more storage."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Window animation scale"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Transition animation scale"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator duration scale"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Never"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duration"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(No name)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index fd84077..7411487 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -408,6 +408,12 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Allow loading GPU debug layers for debug apps"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Enable verbose vendor logging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Include additional device-specific vendor logs in bug reports, which may contain private information, use more battery, and/or use more storage."</string>
+ <string name="enable_verbose_vendor_logging_checkbox" msgid="3864578373293835530">"Disable after one day"</string>
+ <string name="verbose_vendor_logging_notification_title" msgid="6811217272559843592">"Verbose vendor logging has ended"</string>
+ <string name="verbose_vendor_logging_notification_summary" msgid="5226524769774370942">"Enabled for one day"</string>
+ <string name="verbose_vendor_logging_notification_action" msgid="1190831050259046071">"Enable for one more day"</string>
+ <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"Disables after one day"</string>
+ <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Enabled indefinitely"</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Window animation scale"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Transition animation scale"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator duration scale"</string>
@@ -557,6 +563,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms & reminders"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Never"</string>
@@ -569,6 +576,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duration"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(No name)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"This tablet"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index b96dd33..3c95fd2 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite cargar capas de depuración de GPU para apps de depuración"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Habilitar registro detallado"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Incluye otros registros de proveedor específicos del dispositivo en los informes de errores, que podrían contener información privada, consumir más batería o usar más espacio de almacenamiento."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animación de ventana"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animación de transición"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duración de animador"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta app establezca alarmas y programe acciones para horarios específicos. De esta manera, la app puede ejecutarse en segundo plano, lo que podría aumentar el consumo de batería.\n\nSi se desactiva este permiso, no funcionarán las alarmas ni los eventos basados en el tiempo existentes que programe esta app."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"No interrumpir"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar No interrumpir"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nunca"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duración"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sin nombre)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 0b96faa..28bfb38 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite cargar capas de depuración de GPU en aplicaciones de depuración"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Habilitar registro de proveedor detallado"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Incluye otros registros de proveedor específicos del dispositivo en informes de errores, lo que puede añadir información privada, usar más batería u ocupar más espacio de almacenamiento."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animación de ventana"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animación de transición"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duración de animación"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación programe alarmas y otras acciones que se llevan a cabo a una hora determinada. Esto hace que la aplicación pueda seguir activa en segundo plano, lo que puede usar más batería.\n\nSi este permiso está desactivado, no funcionarán las alarmas ni los eventos que se activan a una hora determinada que programe esta aplicación."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"No molestar"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar el modo No molestar"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nunca"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duración"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sin nombre)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tablet"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 42c20399..25bc487 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"GPU silumise kihtide laadimise lubamine silumisrakendustele"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Luba paljusõnaline logimine"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Veaaruannetesse kaasatakse täiendavad seadmepõhised teenusepakkuja logid, mis võivad sisaldada privaatset teavet, kasutada rohkem akut ja/või salvestusruumi."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Akna animatsioonimastaap"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Ülemineku animatsioonimastaap"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animaatori kestuse mastaap"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmid ja meeldetuletused"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata alarme ja ajastada ajakriitilisi toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad alarmid ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajakava, äratus, meeldetuletus, kell"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Mitte segada"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Lülita sisse"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Valiku Mitte segada sisselülitamine"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Mitte kunagi"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Kestus"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Küsi iga kord"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Kuni välja lülitate"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Nimi puudub)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"See telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"See tahvelarvuti"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 6376ad5..92c7d39 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Eman GPUaren arazketa-geruzak kargatzeko baimena arazketa-aplikazioei"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Gaitu saltzaileen erregistro xehatuak"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Sartu gailuaren berariazko saltzaileen erregistro gehigarriak akatsen txostenetan; baliteke haiek informazio pribatua izatea, bateria gehiago erabiltzea eta/edo biltegiratzeko toki gehiago hartzea."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Leihoen animazio-eskala"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Trantsizioen animazio-eskala"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animatzailearen iraupen-eskala"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmak eta abisuak"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nBaimen hori ematen ez baduzu, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programazioa, alarma, abisua, erlojua"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ez molestatzeko modua"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktibatu"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktibatu ez molestatzeko modua"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Inoiz ez"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Iraupena"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Galdetu beti"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Zuk desaktibatu arte"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Izengabea)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefono hau"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tableta hau"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 6eb8bd1..27e203b 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -64,7 +64,7 @@
<string name="connected_via_network_scorer" msgid="7665725527352893558">"اتصال خودکار ازطریق %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده ردهبندی شبکه"</string>
<string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="tap_to_sign_up" msgid="5356397741063740395">"برای ثبتنام ضربه بزنید"</string>
+ <string name="tap_to_sign_up" msgid="5356397741063740395">"برای ثبتنام تکضرب بزنید"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"عدم اتصال به اینترنت"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"سرور DNS خصوصی قابل دسترسی نیست"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
@@ -74,7 +74,7 @@
<string name="osu_opening_provider" msgid="4318105381295178285">"درحال بازکردن <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"متصل نشد"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"درحال تکمیل ثبتنام…"</string>
- <string name="osu_sign_up_failed" msgid="5605453599586001793">"ثبتنام تکمیل نشد. برای امتحان مجدد ضربه بزنید."</string>
+ <string name="osu_sign_up_failed" msgid="5605453599586001793">"ثبتنام تکمیل نشد. برای امتحان مجدد تکضرب بزنید."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ثبتنام کامل شد. درحال اتصال…"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"آهسته"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"تأیید"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"مجاز کردن بارگیری لایههای اشکالزدایی GPU برای برنامههای اشکالزدایی"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"فعال کردن گزارشگیری مفصل فروشنده"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"شامل گزارشات اشکال تکمیلی ورود به سیستم فروشنده ویژه دستگاه میشود که ممکن است دربرگیرنده اطلاعات خصوصی، استفاده بیشتر از باتری، و/یا استفاده بیشتر از فضای ذخیرهسازی باشد."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"مقیاس پویانمایی پنجره"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"مقیاس پویانمایی انتقالی"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"مقیاس طول مدت انیماتور"</string>
@@ -428,7 +440,7 @@
<string name="enable_freeform_support_summary" msgid="1822862728719276331">"فعال کردن پشتیبانی برای پنجرههای آزاد آزمایشی."</string>
<string name="local_backup_password_title" msgid="4631017948933578709">"گذرواژه پشتیبانگیری محلی"</string>
<string name="local_backup_password_summary_none" msgid="7646898032616361714">"پشتیبانگیری کامل رایانه درحال حاضر محافظت نمیشود"</string>
- <string name="local_backup_password_summary_change" msgid="1707357670383995567">"برای تغییر یا حذف گذرواژه برای نسخههای پشتیبان کامل رایانهای ضربه بزنید"</string>
+ <string name="local_backup_password_summary_change" msgid="1707357670383995567">"برای تغییر یا حذف گذرواژه برای نسخههای پشتیبان کامل رایانهای تکضرب بزنید"</string>
<string name="local_backup_password_toast_success" msgid="4891666204428091604">"گذرواژه جدید نسخهٔ پشتیبان تنظیم شد"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="2994718182129097733">"گذرواژه جدید و تأیید آن با یکدیگر مطابقت ندارند"</string>
<string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"گذرواژه پشتیبانگیری تنظیم نشد"</string>
@@ -444,8 +456,8 @@
<item msgid="1282170165150762976">"رنگهای بهینهشده برای محتوای دیجیتالی"</item>
</string-array>
<string name="inactive_apps_title" msgid="5372523625297212320">"برنامههای آماده بهکار"</string>
- <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیرفعال. برای تغییر حالت ضربه بزنید."</string>
- <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال. برای تغییر حالت ضربه بزنید."</string>
+ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیرفعال. برای تغییر حالت تکضرب بزنید."</string>
+ <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال. برای تغییر حالت تکضرب بزنید."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"وضعیت حالت آماده بهکار برنامه:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="transcode_settings_title" msgid="2581975870429850549">"تنظیمات تراتبدیل رسانه"</string>
<string name="transcode_user_control" msgid="6176368544817731314">"ملغی کردن پیشفرضهای تراتبدیل"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"زنگهای ساعت و یادآوریها"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"به این برنامه اجازه میدهد زنگ ساعت تنظیم کند و کنشهای حساس به زمان را زمانبندی کند. این تنظیم به برنامه اجازه میدهد در پسزمینه اجرا شود که ممکن است باتری بیشتری مصرف کند.\n\nاگر این اجازه خاموش باشد، زنگهای ساعت موجود و رویدادهای زمانمحور که این برنامه زمانبندی کرده است کار نخواهند کرد."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"زمانبندی، زنگ ساعت، یادآوری، ساعت"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"مزاحم نشوید"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"روشن کردن"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"روشن کردن «مزاحم نشوید»"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"هرگز"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"مدت"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"هربار پرسیده شود"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"تا زمانیکه آن را خاموش کنید"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(بدون نام)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"هماکنون"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"این تلفن"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"این رایانه لوحی"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index a0dc830..a8222b9 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Salli GPU:n virheenkorjauskerrosten lataus"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Käytä laajennettua kirjausta"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Sisällytä virheraportteihin muita laitekohtaisia myyjälokeja, jotka voivat sisältää yksityisiä tietoja, käyttää enemmän akkua ja/tai käyttää enemmän tallennustilaa"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Ikkuna"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Siirtymä"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animaattori"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Herätykset ja muistutukset"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Anna sovelluksen lisätä herätyksiä ja ajoittaa kiireellisiä tapahtumia. Näin sovellus voi toimia taustalla, mikä voi kuluttaa enemmän virtaa.\n\nIlman tätä lupaa sovelluksen ajoittamat herätykset ja aikaan perustuvat tapahtumat eivät toimi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajoitus, herätys, muistutus, kello"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Älä häiritse"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ota käyttöön"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Laita Älä häiritse ‑tila päälle"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ei koskaan"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Kesto"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Kysy aina"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Kunnes laitat pois päältä"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Ei nimeä)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tämä puhelin"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tämä tabletti"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 0e03d18..cdc1e44 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Autoriser couches débogage GPU pour applis de débogage"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Activer le journal détaillé des fournisseurs"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Incluez les journaux supplémentaires du fournisseur propres à l\'appareil dans les rapports de bogue. Ils peuvent contenir des données personnelles, épuiser la pile plus rapidement et/ou utiliser plus d\'espace de stockage."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Échelle animation fenêtres"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Éch. d\'animation des transitions"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Échelle durée animation"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autorisez cette application à créer des alarmes et à programmer des actions urgentes. Cela permet à l’application de s\'exécuter en arrière-plan, ce qui peut nécessiter plus de pile.\n\nSi cette autorisation est désactivée, les alarmes existantes et les événements en temps réel programmés par cette application ne fonctionneront pas."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"horaire, alarme, rappel, horloge"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne pas déranger"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Jamais"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durée"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sans nom)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index ed4bf48..300dca1 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Autoriser le chargement de couches de débogage GPU"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Activer la journalisation détaillée du fournisseur"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclure les journaux supplémentaires du fournisseur, spécifiques à l\'appareil, dans les rapports de bug. Ils peuvent contenir des informations personnelles, solliciter davantage la batterie et/ou utiliser plus d\'espace de stockage."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Échelle d\'animation des fenêtres"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Échelle d\'animation des transitions"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Échelle de durée d\'animation"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autoriser cette appli à définir des alarmes et à programmer des actions à certaines heures. Elle s\'exécutera alors en arrière-plan, ce qui peut solliciter davantage la batterie.\n\nSi l\'autorisation est désactivée, les alarmes existantes et les événements programmés par l\'appli ne fonctionneront pas."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"définir, alarme, rappel, horloge"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne pas déranger"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Jamais"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durée"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à ce que vous le désactiviez"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sans nom)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Cette tablette"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 3a08d2f..d00d6d3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite cargar capas de depuración da GPU para aplicacións de depuración"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Activar rexistro de provedores"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclúe outros rexistros de provedores específicos do dispositivo en informes de erros; pode conter información privada, consumir máis batería e ocupar máis espazo de almacenamento"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animación da ventá"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala animación-transición"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala duración animador"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas e recordatorios"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación defina alarmas e planifique accións que dependan da hora. Con este permiso, a aplicación pode executarse en segundo plano, o que pode provocar un maior consumo de batería.\n\nSe este permiso está desactivado, non funcionarán as alarmas que xa se definisen nin os eventos que dependan da hora planificados por esta aplicación."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planificar, alarma, recordatorio, reloxo"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Non molestar"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar modo Non molestar"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nunca"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duración"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar sempre"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Ata a desactivación"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sen nome)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Esta tableta"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 07eda9a..0c204f7 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ડિબગ ઍપ માટે GPU ડિબગ સ્તરો લોડ કરવાની મંજૂરી આપો"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"વર્બોઝ વેન્ડર લૉગિંગ ચાલુ કરો"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"ખામીની જાણકારીમાં ડિવાઇસથી જોડાયેલા ચોક્કસ વેન્ડર લૉગ શામેલ કરો, જેમાં ખાનગી માહિતી શામેલ હોઈ શકે છે, તે વધુ બૅટરીનો ઉપયોગ કરી શકે છે અને/અથવા વધુ સ્ટોરેજનો ઉપયોગ કરી શકે છે."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"વિન્ડો ઍનિમેશન સ્કેલ"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ટ્રાન્ઝિશન ઍનિમેશન સ્કેલ"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ઍનિમેટર અવધિ સ્કેલ"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"અલાર્મ અને રિમાઇન્ડર"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"આ ઍપને અલાર્મ સેટ કરવા અને સમય પ્રતિ સંવેદનશીલ ક્રિયાઓ શેડ્યૂલ કરવા માટે મંજૂરી આપો. આ ઍપને બૅકગ્રાઉન્ડમાં ચાલવા દે છે, જેને કારણે બૅટરીનો વધુ વપરાશ થઈ શકે છે.\n\nજો આ પરવાનગી બંધ હોય, તો આ ઍપ દ્વારા શેડ્યૂલ કરવામાં આવેલા વર્તમાન અલાર્મ અને સમય આધારિત ઇવેન્ટ કામ કરશે નહીં."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"શેડ્યૂલ, અલાર્મ, રિમાઇન્ડર, ઘડિયાળ"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ખલેલ પાડશો નહીં"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ચાલુ કરો"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ખલેલ પાડશો નહીં ચાલુ કરો"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ક્યારેય નહીં"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"અવધિ"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"દર વખતે પૂછો"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"તમે બંધ ન કરો ત્યાં સુધી"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(નામ નથી)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"આ ફોન"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"આ ટૅબ્લેટ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 7db157e..2c938a6 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"डीबग ऐप के लिए जीपीयू डीबग लेयर लोड करने दें"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"वर्बोस वेंडर लॉगिंग चालू करें"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"गड़बड़ियों की रिपोर्ट में डिवाइस से जुड़े अतिरिक्त वेंडर लॉग शामिल करें. इन लॉग में निजी जानकारी, बैटरी का ज़्यादा इस्तेमाल, और/या डिवाइस का स्टोरेज ज़्यादा इस्तेमाल करने की जानकारी हो सकती है."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"विंडो ऐनिमेशन स्केल"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ट्रांज़िशन ऐनिमेशन स्केल"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ऐनिमेटर ड्यूरेशन स्केल"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म और रिमाइंडर"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"इस ऐप्लिकेशन को अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर सेट करने की अनुमति दें. ऐसा करने से, ऐप्लिकेशन को बैकग्राउंड में चलने की अनुमति मिलती है. इससे बैटरी ज़्यादा खर्च होती है.\n\nऐप्लिकेशन को यह अनुमति न देने पर, इसकी मदद से सेट किए गए अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर काम नहीं करेंगे."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्यूल, अलार्म, रिमाइंडर, घड़ी"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"परेशान न करें"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"चालू करें"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'परेशान न करें\' चालू करें"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"कभी नहीं"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"अवधि"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"हर बार पूछें"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"जब तक इसे बंद नहीं किया जाता"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(कोई नाम नहीं)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"अभी-अभी"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"यह फ़ोन"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यह टैबलेट"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index ef0d589..260e9c2 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Omogućuje učitavanje slojeva za otklanjanje pogrešaka GPU-a za aplikacije za otklanjanje pogrešaka"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Omogući opširni zapisnik"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Uključite dodatne zapisnike dobavljača pojedinog uređaja u izvješća o programskim pogreškama koja mogu sadržavati privatne podatke, trošiti više baterije i/ili zauzeti više prostora za pohranu."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Brzina animacije prozora"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Brzina animacije prijelaza"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Razmjer duljine animatora"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Omogućite toj aplikaciji da postavlja alarme i zakazuje radnje u točno određeno vrijeme. To aplikaciji omogućuje da se izvodi u pozadini, pa je moguća dodatna potrošnja baterije.\n\nAko je to dopuštenje isključeno, postojeći alarmi i događaji zakazani putem te aplikacije neće funkcionirati."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne uznemiravaj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključite opciju Ne uznemiravaj."</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nikada"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trajanje"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Do isključivanja"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Bez imena)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ovaj tablet"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index fb142f6..b16f4a3 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"GPU-hibakeresési rétegek betöltésének engedélyezése"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Részletes szolgáltatói naplózás engedélyezése"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"További eszközspecifikus szolgáltatói naplók felvétele a hibajelentésekbe. Ezek a naplók tartalmazhatnak privát információkat, ezenkívül előfordulhat, hogy jobban merítik az akkumulátort, illetve nagyobb tárhelyet foglalnak el."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Ablakanimáció tempója"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Áttűnési animáció tempója"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animáció tempója"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ébresztések és emlékeztetők"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lehetővé teszi ennek az alkalmazásnak, hogy ébresztéseket állítson be és időérzékeny feladatokat ütemezzen. Ezzel engedélyezi az alkalmazásnak, hogy a háttérben fusson, ami megnövekedett akkumulátorhasználattal járhat.\n\nHa ez az engedély ki van kapcsolva, az alkalmazás által beállított ébresztések és ütemezett időérzékeny események nem fognak működni."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ütemezés, ébresztés, emlékeztető, óra"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne zavarjanak"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Bekapcsolás"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"A Ne zavarjanak mód bekapcsolása"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Soha"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Időtartam"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Mindig kérdezzen rá"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Kikapcsolásig"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Nincs név)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ez a telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ez a táblagép"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index e7cbf2d..bd9e564 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -237,7 +237,7 @@
<string name="choose_profile" msgid="343803890897657450">"Ընտրեք պրոֆիլ"</string>
<string name="category_personal" msgid="6236798763159385225">"Անձնական"</string>
<string name="category_work" msgid="4014193632325996115">"Աշխատանքային"</string>
- <string name="category_private" msgid="4244892185452788977">"Անձնական"</string>
+ <string name="category_private" msgid="4244892185452788977">"Մասնավոր"</string>
<string name="category_clone" msgid="1554511758987195974">"Կլոն"</string>
<string name="development_settings_title" msgid="140296922921597393">"Մշակողի ընտրանքներ"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Միացնել մշակողի ընտրանքները"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Թույլատրել GPU վրիպազերծման շերտերի բեռնումը վրիպազերծման հավելվածների համար"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Մատակարարի մանրամասն գրանցամատյան"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Վրիպակների հաշվետվություններում ներառել կոնկրետ սարքի վերաբերյալ մատակարարի լրացուցիչ մատյանները։ Դա կարող է պարունակել խիստ անձնական տեղեկություններ, ավելի արագ սպառել մարտկոցի լիցքը և/կամ ավելի շատ տարածք օգտագործել։"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Պատուհանի շարժապատկեր"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Անցումների շարժապատկեր"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Շարժանկարի տևողության սանդղակ"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Զարթուցիչներ և հիշեցումներ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Թույլատրել այս հավելվածին զարթուցիչներ կարգավորել և որոշակի ժամանակի համար գործողություններ պլանավորել։ Այդ դեպքում հավելվածն աշխատում է ֆոնային ռեժիմում, և արդյունքում մարտկոցի լիցքն ավելի արագ է սպառվում։\n\nԵթե այս թույլտվությունն անջատված է, հավելվածի կողմից կարգավորված զարթուցիչները և գործողությունների ժամանակացույցները չեն աշխատի։"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ժամանակացույց, զարթուցիչ, հիշեցում, ժամացույց"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Չանհանգստացնել"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Միացնել"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Միացրեք «Չանհանգստացնել» ռեժիմը"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Երբեք"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Տևողություն"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ամեն անգամ հարցնել"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Մինչև անջատեք"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Անանուն)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Այս հեռախոսը"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Այս պլանշետը"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 82a390e..b3a1291 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Izinkan lapisan debug GPU dimuat di aplikasi debug"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Aktifkan logging vendor panjang"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Sertakan log vendor tambahan khusus perangkat dalam laporan bug, yang mungkin berisi informasi pribadi. Meningkatkan penggunaan baterai dan/atau ruang penyimpanan."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Skala animasi jendela"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Skala animasi transisi"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Skala durasi animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm & pengingat"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Mengizinkan aplikasi ini menyetel alarm dan menjadwalkan tindakan yang sensitif waktu. Hal ini memungkinkan aplikasi berjalan di latar belakang, sehingga mungkin menggunakan lebih banyak daya baterai.\n\nJika izin ini dinonaktifkan, alarm dan acara berbasis waktu yang dijadwalkan oleh aplikasi ini tidak akan berfungsi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadwal, alarm, pengingat, jam"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Jangan Ganggu"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktifkan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktifkan mode Jangan Ganggu"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Tidak pernah"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durasi"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Selalu tanya"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Sampai Anda menonaktifkannya"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Tanpa nama)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ponsel ini"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 47cdcca..7712b8a 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Leyfa villuleit skjákorts fyrir villuleit forrita"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Nákvæm skráning söluaðila"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Taka með viðbótarannála söluaðila fyrir tiltekin tæki í villutilkynningum, sem gætu innihaldið viðkvæmar upplýsingar, notað meiri rafhlöðuorku og/eða þurft meira geymslupláss."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Kvarði gluggahreyfinga"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Lengd hreyfiumbreytinga"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Tímalengd hreyfiáhrifa"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Vekjarar og áminningar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leyfa þessu forriti að stilla vekjara og áætla aðgerðir sem þurfa að eiga sér stað innan ákveðins tímaramma. Þetta leyfir forritinu að keyra í bakgrunninum sem getur notað meiri rafhlöðuorku.\n\nEf slökkt er á þessari heimild munu núverandi vekjarar og tímasettir viðburðir sem þetta forrit stillir ekki virka."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"áætlun, vekjari, áminning, klukka"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ónáðið ekki"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Kveikja"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Kveikja á „Ónáðið ekki“"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Aldrei"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Lengd"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spyrja í hvert skipti"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Þar til þú slekkur"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Nafn vantar)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Þessi sími"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Þessi spjaldtölva"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 3714593..a661700 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Consenti caricamento livelli debug GPU per app di debug"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Attiva log dettagliati fornitori"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Includi log aggiuntivi di fornitori relativi a un dispositivo specifico nelle segnalazioni di bug che potrebbero contenere informazioni private, causare un maggior consumo della batteria e/o utilizzare più spazio di archiviazione"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Scala animazione finestra"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Scala animazione transizione"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Scala durata animatore"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Sveglie e promemoria"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Consenti a questa app di impostare sveglie e programmare azioni per orari specifici. L\'app potrà essere eseguita in background, comportando un consumo maggiore della batteria.\n\nSe questa autorizzazione viene disattivata, le sveglie esistenti e gli eventi basati sull\'orario programmati da questa app non funzioneranno."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programmare, sveglia, promemoria, orologio"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Non disturbare"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Attiva"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Attiva Non disturbare"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Mai"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durata"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Chiedi ogni volta"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Nessun nome)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Questo smartphone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Questo tablet"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index c2f4a8f..fe674bc 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -125,7 +125,7 @@
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"אודיו של מדיה"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"שיחות טלפון"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"התקן קלט"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"מכשיר לקליטת נתונים"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"גישה לאינטרנט"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"אישור גישה אל אנשי הקשר והיסטוריית השיחות"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"המידע הזה ישמש למשל כדי להשמיע התראות על שיחות נכנסות"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"טעינת שכבות לניפוי באגים ב-GPU לאפליקציות ניפוי באגים"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"הפעלת רישום ספקים מפורט ביומן"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"הוספת רישומי יומן של יצרנים למכשירים ספציפיים בדוחות על באגים. דוחות אלה עשויים להכיל מידע פרטי, להגביר את צריכת הסוללה ולצרוך נפח אחסון גדול יותר."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"קנה מידה לאנימציה של חלון"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"קנה מידה לאנימציית מעבר"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"קנה מידה למשך זמן אנימציה"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"שעונים מעוררים ותזכורות"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ההרשאה הזו מתירה לאפליקציה להגדיר שעון מעורר ולתזמן פעולות דחופות. האפליקציה תוכל לפעול ברקע ובכך להגביר את צריכת הסוללה.\n\nאם ההרשאה מושבתת, ההתראות והאירועים מבוססי-הזמן שהוגדרו ותוזמנו על ידי האפליקציה לא יפעלו."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"תזמון, שעון מעורר, תזכורת, שעון"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"נא לא להפריע"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"הפעלה"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"הפעלת מצב נא לא להפריע"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"אף פעם"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"משך זמן"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"יש לשאול בכל פעם"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(ללא שם)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"הטלפון הזה"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"הטאבלט הזה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 17e902e..f5ee5c4 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"デバッグアプリに GPU デバッグレイヤの読み込みを許可"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"ベンダーの詳細なロギングを有効にする"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"バグレポートには、その他のデバイス固有のベンダーログが含まれます。これには、非公開の情報が含まれることがあります。また、バッテリーやストレージの使用量が増えることもあります。"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"ウィンドウ アニメ スケール"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"トランジション アニメ スケール"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator 再生時間スケール"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"アラームとリマインダー"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"アラームの設定や時間ベースのアクション設定を、このアプリに許可します。これによりアプリがバックグラウンドで実行できるようになるため、バッテリーの使用量が増えることがあります。\n\nこの権限が OFF の場合、このアプリで設定された既存のアラームと時間ベースのイベントは機能しなくなります。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"スケジュール, アラーム, リマインダー, 時計"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"サイレント モード"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ON にする"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"サイレント モードを ON にする"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"なし"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"時間"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"毎回確認"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"OFF にするまで"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(名前なし)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"たった今"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"このデバイス"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"このタブレット"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 4efebc5..1c5822c 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -412,6 +412,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"გასამართი აპებისთვის GPU-ს შეცდომების გამართვის შრეების გაშვების დაშვება"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"ჟურნალებში მომწოდებელთა დაწვრილებითი აღრიცხვის ჩართვა"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"გამყიდველის მოწყობილობისთვის სპეციფიკური დამატებითი ჟურნალები შევიდეს სისტემის ხარვეზის ანგარიშებში, რომლებიც შეიძლება შეიცავდეს პირად ინფორმაციას, ხარჯავდეს ბატარეის მეტ მუხტს და/ან იყენებდეს მეტ მეხსიერებას."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"ფანჯარა: მასშტაბი"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"გადასვლის მასშტაბი"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ანიმაციების ხანგრძლივობის მასშტაბი"</string>
@@ -561,6 +573,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"მაღვიძარები და შეხსენებები"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ნებას რთავს ამ აპს, დააყენოს მაღვიძარები და დაგეგმოს დროზე დამოკიდებული მოქმედებები. ეს საშუალებას აძლევს აპს, იმუშაოს ფონურად, რამაც შეიძლება ბატარეის ხარჯი გაზარდოს.\n\nთუ ეს ნებართვა გამორთულია, ამ აპით დაგეგმილი მაღვიძარები და დროზე დამოკიდებული მოვლენები არ იმუშავებს."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"განრიგი, მაღვიძარა, შეხსენება, საათი"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"არ შემაწუხოთ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ჩართვა"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„არ შემაწუხოთ“ რეჟიმის ჩართვა"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"არასოდეს"</string>
@@ -573,6 +586,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ხანგრძლივობა"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ყოველთვის მკითხეთ"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"გამორთვამდე"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(უსახელო)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ეს ტელეფონი"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ამ ტაბლეტზე"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index a1ea070..ef9b005 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -58,7 +58,7 @@
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\"<xliff:g id="AP_NAME">%1$s</xliff:g>\" қолданбасына қосылу мүмкін емес"</string>
<string name="wifi_check_password_try_again" msgid="8817789642851605628">"Құпия сөзді тексеріп, әрекетті қайталаңыз"</string>
<string name="wifi_not_in_range" msgid="1541760821805777772">"Аумақта жоқ"</string>
- <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string>
+ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты түрде қосылмайды"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"GPU түзету қабаттарының жүктелуіне рұқсат ету"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Жеткізуші туралы толық мәліметті тіркеу"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Қате туралы есепте жеткізушінің құрылғыға қатысты қосымша ақпараты қамтылады. Мұнда жеке ақпарат көрсетілуі, батарея шығыны артуы және/немесе қосымша жад пайдаланылуы мүмкін."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Терезе анимациясының өлшемі"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Ауысу анимациясының өлшемі"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Аниматор ұзақтығы"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Оятқыштар мен еске салғыштар"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бұл қолданбаға оятқыштарды орнатуға және уақытқа байланысты әрекеттерді жоспарлауға рұқсат береді. Мұндайда қолданба фондық режимде жұмыс істейді, сондықтан батарея шығыны артуы мүмкін.\n\nБұл рұқсат өшірулі болса, осы қолданбада жоспарланған ағымдағы оятқыштар мен уақытқа байланысты іс-шаралар жұмыс істемейді."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"кесте, оятқыш, еске салғыш, сағат"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Мазаламау"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Қосу"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Мазаламау режимін қосу"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ешқашан"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Ұзақтығы"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Әрдайым сұрау"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Аты берілмеген)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Осы телефон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Осы планшет"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 431bdf7..199b114 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"អនុញ្ញាតឱ្យផ្ទុកស្រទាប់ជួសជុល GPU សម្រាប់កម្មវិធីជួសជុល"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"បើកការកត់ត្រាអ្នកផ្គត់ផ្គង់ឥតសំចៃ"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"រួមបញ្ចូលកំណត់ហេតុអ្នកផ្គត់ផ្គង់ពាក់ព័ន្ធនឹងឧបករណ៍បន្ថែមទៀតនៅក្នុងរបាយការណ៍អំពីបញ្ហា ដែលអាចមានផ្ទុកព័ត៌មានឯកជន ប្រើប្រាស់ថ្មច្រើនជាង និង/ឬប្រើប្រាស់ទំហំផ្ទុកច្រើនជាង។"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"មាត្រដ្ឋានចលនាវិនដូ"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"មាត្រដ្ឋានដំណើរផ្លាស់ប្ដូរចលនា"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"មាត្រដ្ឋានរយៈពេលនៃកម្មវិធីចលនា"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ម៉ោងរោទ៍ និងការរំលឹក"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"អនុញ្ញាតឱ្យកម្មវិធីនេះកំណត់ម៉ោងរោទ៍ និងកំណត់កាលវិភាគសកម្មភាពដែលតម្រូវឱ្យទាន់ពេលវេលា។ ការធ្វើបែបនេះអនុញ្ញាតឱ្យកម្មវិធីនេះដំណើរការនៅផ្ទៃខាងក្រោយ ដែលអាចប្រើថ្មកាន់តែច្រើន។\n\nប្រសិនបើបិទការអនុញ្ញាតនេះ ម៉ោងរោទ៍ដែលមានស្រាប់ និងព្រឹត្តិការណ៍ផ្អែកលើពេលវេលាដែលកំណត់ដោយកម្មវិធីនេះនឹងមិនដំណើរការទេ។"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"កាលវិភាគ ម៉ោងរោទ៍ ការរំលឹក នាឡិកា"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"កុំរំខាន"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"បើក"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"បើកមុខងារកុំរំខាន"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"កុំឱ្យសោះ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"រយៈពេល"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"សួរគ្រប់ពេល"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"រហូតទាល់តែអ្នកបិទ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(គ្មានឈ្មោះ)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"អម្បាញ់មិញ"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ថេប្លេតនេះ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a25e179..6ff80d8 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ಡೀಬಗ್ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗಾಗಿ GPU ಡೀಬಗ್ ಲೇಯರ್ಗಳನ್ನು ಲೋಡ್ ಮಾಡುವುದನ್ನು ಅನುಮತಿಸಿ"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"ವರ್ಬೋಸ್ ವೆಂಡರ್ ಲಾಗಿಂಗ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"ಬಗ್ ವರದಿಗಳಲ್ಲಿ ಹೆಚ್ಚುವರಿ ಸಾಧನ ನಿರ್ದಿಷ್ಟ ವೆಂಡರ್ ಲಾಗ್ಗಳು ಒಳಗೊಂಡಿದೆ, ಇದು ಖಾಸಗಿ ಮಾಹಿತಿ, ಹೆಚ್ಚಿನ ಬ್ಯಾಟರಿ ಬಳಕೆ ಮತ್ತು/ಅಥವಾ ಹೆಚ್ಚಿನ ಸಂಗ್ರಹಣೆಯ ಬಳಕೆಯನ್ನು ಒಳಗೊಂಡಿರಬಹುದು."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Window ಅನಿಮೇಶನ್ ಸ್ಕೇಲ್"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ಪರಿವರ್ತನೆ ಅನಿಮೇಶನ್ ಸ್ಕೇಲ್"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ಅನಿಮೇಟರ್ ಅವಧಿಯ ಪ್ರಮಾಣ"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ಅಲಾರಂಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳು"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ಅಲಾರಂಗಳನ್ನು ಹೊಂದಿಸಲು ಮತ್ತು ಸಮಯ-ಸೂಕ್ಷ್ಮವಾದ ಕ್ರಿಯೆಗಳನ್ನು ನಿಗದಿಪಡಿಸಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ. ಇದು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಅದರಿಂದ ಹೆಚ್ಚು ಬ್ಯಾಟರಿ ಬಳಕೆಯಾಗಬಹುದು.\n\nಈ ಅನುಮತಿ ಆಫ್ ಆಗಿದ್ದರೆ, ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಲಾರಂಗಳು ಮತ್ತು ಈ ಆ್ಯಪ್ ನಿಗದಿಪಡಿಸಿದ ಸಮಯ-ಸೂಕ್ಷ್ಮ ಈವೆಂಟ್ಗಳು ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ವೇಳಾಪಟ್ಟಿ, ಅಲಾರಂ, ರಿಮೈಂಡರ್, ಗಡಿಯಾರ"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ಆನ್ ಮಾಡಿ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಅನ್ನು ಆನ್ ಮಾಡಿ"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ಎಂದೂ ಇಲ್ಲ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ಅವಧಿ"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(ಯಾವುದೇ ಹೆಸರಿಲ್ಲ)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ಈ ಫೋನ್"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ಈ ಟ್ಯಾಬ್ಲೆಟ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 9cb770f..af5f9d4 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"디버그 앱에 GPU 디버그 레이어 로드 허용"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"상세 공급업체 로깅 사용 설정"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"버그 신고에 추가적인 기기별 공급업체 로그를 포함합니다. 여기에는 개인정보가 포함될 수 있으며, 배터리 또는 저장공간 사용량이 늘어날 수 있습니다."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"창 애니메이션 배율"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"전환 애니메이션 배율"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator 길이 배율"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"알람 및 리마인더"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"이 앱이 알람을 설정하고 시간 기반 작업을 예약할 수 있도록 허용합니다. 이렇게 하면 백그라운드에서 앱 실행이 허용되어 배터리 사용량이 증가할 수 있습니다.\n\n이 권한을 사용 중지하면 이 앱에서 예약한 기존의 알람 및 시간 기반 일정이 작동하지 않습니다."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"일정 예약, 알람, 리마인더, 시계"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"방해 금지 모드"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"사용 설정"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"방해 금지 모드 사용 설정"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"사용 안함"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"지속 시간"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"항상 확인"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"사용 중지할 때까지"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(이름 없음)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"조금 전"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"이 휴대전화"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"이 태블릿"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 64180a9..1d3f44a 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"GPU мүчүлүштүктөрдү оңдоо катмарларын иштетет"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Кызмат көрсөтүүчүнүн журналы"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Кызмат көрсөтүүчүнүн түзмөккө байланыштуу кошумча жазуулары мүчүлүштүк тууралуу кабарларга кошулат. Анда купуя маалымат камтылып, батарея тезирээк отуруп жана/же сактагычтан көбүрөөк орун ээлеши мүмкүн."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Терезелердин анимациясы"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Өткөрүү анимацснн шкаласы"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Анимациянын узактыгы"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ойготкучтар жана эстеткичтер"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бул колдонмого ойготкучтарды коюуга жана башка аракеттерди графикке киргизүүгө уруксат бересиз. Ушуну менен колдонмо фондо иштеп, батареяны көбүрөөк сарпташы мүмкүн.\n\nЭгер бул уруксат өчүрүлсө, колдонмодогу ойготкучтар жана графикке киргизилген башка аракеттер иштебейт."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"график, ойготкуч, эстеткич, саат"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Тынчымды алба"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Күйгүзүү"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Тынчымды алба\" режимин күйгүзүү"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Эч качан"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Узактыгы"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ар дайым суралсын"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Бул функция өчүрүлгөнгө чейин"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Аты жок)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ушул телефон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ушул планшет"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index f6a2681..d99759b 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ອະນຸຍາດການໂຫລດຊັ້ນຂໍ້ມູນດີບັກ GPU ສຳລັບແອັບດີບັກ"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"ເປີດໃຊ້ການບັນທຶກຜູ້ຂາຍແບບລະອຽດ"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"ຮວມທັງການລາຍງານຂໍ້ຜິດພາດການເຂົ້າສູ່ລະບົບຂອງຜູ້ຂາຍສະເພາະອຸປະກອນເພີ່ມເຕີມ, ເຊິ່ງອາດມີຂໍ້ມູນສ່ວນຕົວ, ໃຊ້ແບັດເຕີຣີຫຼາຍຂຶ້ນ ແລະ/ຫຼື ໃຊ້ບ່ອນຈັດເກັບຂໍ້ມູນເພີ່ມເຕີມ."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"ຂະໜາດໜ້າຈໍຂອງອະນິເມຊັນ"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ຂະໜາດສະຫຼັບອະນິເມຊັນ"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ໄລຍະເວລາອະນິເມຊັນ"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ອະນຸຍາດໃຫ້ແອັບນີ້ຕັ້ງໂມງປຸກ ແລະ ກຳນົດເວລາຄຳສັ່ງທີ່ເນັ້ນເລື່ອງເວລາເປັນສຳຄັນໄດ້. ນີ້ຈະເຮັດໃຫ້ແອັບເຮັດວຽກໄດ້ໃນພື້ນຫຼັງ, ເຊິ່ງອາດໃຊ້ແບັດເຕີຣີຫຼາຍຂຶ້ນ.\n\nຫາກປິດການອະນຸຍາດນີ້ໄວ້, ໂມງປຸກທີ່ມີຢູ່ກ່ອນແລ້ວ ແລະ ເຫດການທີ່ອ້າງອີງເວລາທີ່ກຳນົດໄວ້ໂດຍແອັບນີ້ຈະບໍ່ສາມາດເຮັດວຽກໄດ້."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ກຳນົດເວລາ, ໂມງປຸກ, ການແຈ້ງເຕືອນ, ໂມງ"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ຫ້າມລົບກວນ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ເປີດ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ເປີດໂໝດຫ້າມລົບກວນ"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ບໍ່ໃຊ້"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ໄລຍະເວລາ"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ຖາມທຸກເທື່ອ"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"ຈົນກວ່າທ່ານຈະປິດ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(ບໍ່ມີຊື່)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ແທັບເລັດນີ້"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 4abde8f..204afc7 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Leisti įkelti graf. proc. der. sluoks. der. progr."</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Įg. daugiaž. pasl. teik. reg."</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Į pranešimus apie riktą įtraukiami papildomi konkretaus įrenginio paslaugų teikėjo žurnalai, kuriuose gali būti privačios informacijos, kurie gali naudoti daugiau akumuliatoriaus energijos ir (arba) daugiau vietos saugykloje."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Lango animacijos mast."</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Animuoto perėjimo mast."</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator. trukmės skalė"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signalai ir priminimai"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leiskite šiai programai nustatyti signalus ir suplanuoti veiksmus, kuriems svarbus laiko veiksnys. Dėl to programa gali veikti fone ir sunaudoti daugiau akumuliatoriaus energijos.\n\nJei šis leidimas išjungtas, šios programos suplanuoti esami signalai ir laiku pagrįsti įvykiai neveiks."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"tvarkaraštis, signalas, priminimas, laikrodis"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Netrukdymo režimas"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Įjungti"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Netrukdymo režimo įjungimas"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Niekada"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trukmė"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Klausti kaskart"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Kol išjungsite"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Nėra pavadinimo)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis telefonas"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetinis kompiuteris"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index a8c3f5a..71d11d5 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Atļaut GPU atkļūd. slāņu ielādi atkļūd. lietotnēm"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Iespējot izvērsto reģistrēšanu"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Iekļaut kļūdu pārskatos konkrētas ierīces papildu nodrošinātāju žurnālus (var iekļaut privātu informāciju, patērēt vairāk akumulatora enerģijas un/vai aizņemt vairāk vietas krātuvē)."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Loga animācijas mērogs"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Pārejas animācijas mērogs"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animācijas ilguma mērogs"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signāli un atgādinājumi"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Atļaujiet šai lietotnei iestatīt signālus un ieplānot darbības, kas jāveic konkrētā laikā. Tādējādi lietotne darbosies fonā un, iespējams, patērēs vairāk akumulatora enerģijas.\n\nJa šī atļauja nav piešķirta, esošie signāli un šīs lietotnes ieplānotie notikumi, kas jāizpilda konkrētā laikā, nedarbosies."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ieplānot, signāls, atgādinājums, pulkstenis"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Netraucēt"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ieslēgt"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Režīma “Netraucēt” ieslēgšana"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nekad"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Ilgums"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vaicāt katru reizi"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Līdz brīdim, kad izslēgsiet"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Nav vārda)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis tālrunis"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Šis planšetdators"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index b2456d3..537f8b5 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Дозволи вчитување GPU-слоеви за отстранув. грешки"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Опширна евиденција на продавачи"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Вклучува дополнителна евиденција на продавачи во извештаите за грешки за конкретен уред, којашто може да содржи приватни податоци, повеќе да ја користи батеријата и/или да користи повеќе капацитет."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Брзина на анимации за прозорци"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Брзина на преодни анимации"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Брзина на општи анимации"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Аларми и потсетници"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дозволете ѝ на апликацијава да поставува аларми и да закажува дејства со временски рокови. Ова овозможува апликацијата да работи во заднина и така може повеќе да ја троши батеријата.\n\nАко дозволава е исклучена, нема да функционираат постојните аларми и настаните според време закажани од апликацијава."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"закажување, аларм, потсетник, часовник"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не вознемирувај"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Вклучи"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Исклучување на „Не вознемирувај“"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Никогаш"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Времетраење"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Прашувај секогаш"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Додека не го исклучите"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Без име)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Пред малку"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овој телефон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овој таблет"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 9cf6268..c096260 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ഡീബഗ് ആപ്പുകൾക്കായി GPU ഡീബഗ് ലെയറുകൾ ലോഡ് ചെയ്യാൻ അനുവദിക്കുക"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"വെർബോസ് വെണ്ടർ ലോഗ് ചെയ്യൽ പ്രവർത്തനക്ഷമമാക്കൂ"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"ബഗ് റിപ്പോർട്ടുകളിൽ ഉപകരണ-നിർദ്ദിഷ്ട വെണ്ടർ അധിക ലോഗുകൾ ഉൾപ്പെടുത്തുക, അതിൽ സ്വകാര്യ വിവരങ്ങൾ അടങ്ങിയിരിക്കാം, കൂടുതൽ ബാറ്ററി ഉപയോഗിക്കാം കൂടാതെ/അല്ലെങ്കിൽ കൂടുതൽ സ്റ്റോറേജ് ഇടം ഉപയോഗിക്കാം."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"വിൻഡോ ആനിമേഷൻ സ്കെയിൽ"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ട്രാൻസിഷൻ ആനിമേഷൻ സ്കെയിൽ"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ആനിമേറ്റർ ദൈർഘ്യ സ്കെയിൽ"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"അലാറങ്ങളും റിമെെൻഡറുകളും"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും സമയപ്രാധാന്യമുള്ള പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. പശ്ചാത്തലത്തിൽ റൺ ചെയ്യാൻ ഇത് ഈ ആപ്പിന് അനുവാദം നൽകുന്നു, ഇതിന് കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാം.\n\nഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് നിലവിൽ ഷെഡ്യൂൾ ചെയ്ത അലാറങ്ങളും സമയാധിഷ്ഠിത ഇവന്റുകളും പ്രവർത്തിക്കില്ല."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ഷെഡ്യൂൾ, അലാറം, റിമെെൻഡർ, ക്ലോക്ക്"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ശല്യപ്പെടുത്തരുത്"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ഓണാക്കുക"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ശല്യപ്പെടുത്തരുത്\' ഓണാക്കുക"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ഒരിക്കലും വേണ്ട"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ദൈർഘ്യം"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"എപ്പോഴും ചോദിക്കുക"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(പേരില്ല)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ഈ ഫോൺ"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ഈ ടാബ്ലെറ്റ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 436326e..b3bca99 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Дебаг хийх аппад GPU дебаг хийх давхарга ачаалахыг зөвшөөрөх"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Нийлүүлэгчийн дэлгэрэнгүй логийг идэвхжүүлэх"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Төхөөрөмжийн тодорхойлсон нийлүүлэгчийн нэвтрэх үеийн алдааны нэмэлт мэдээг оруулах бөгөөд энэ нь хувийн мэдээлэл агуулж, батарейг илүү ашиглах болон/эсвэл хадгалах сан илүү ашиглаж болзошгүй."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Цонхны анимацийн масштаб"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Шилжилтийн анимацийн масштаб"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Анимацийн хугацааны масштаб"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Сэрүүлэг, сануулагч"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Энэ аппад сэрүүлэг тавих болон хугацаанд мэдрэг үйлдлийн хуваарь гаргахыг зөвшөөрнө үү. Энэ нь аппад ард ажиллахыг зөвшөөрөх бөгөөд ингэснээр илүү их батарей ашиглаж магадгүй.\n\nХэрэв энэ зөвшөөрөл унтраалттай бол энэ аппын аль хэдийн тавьсан сэрүүлэг болон хуваарь гаргасан хугацаанд мэдрэг үйл явдал ажиллахгүй."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"хуваарь, сэрүүлэг, сануулагч, цаг"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Бүү саад бол"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Асаах"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Бүү саад бол горимыг асаах"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Хэзээ ч үгүй"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Хугацаа"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Тухай бүрд асуух"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Таныг унтраах хүртэл"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Нэр байхгүй)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Дөнгөж сая"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Энэ утас"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Энэ таблет"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index b68fc71..f6467fa 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"डीबग अॅप्ससाठी GPU डीबग स्तर लोड करण्याची अनुमती द्या"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"व्हर्बोझ विक्रेता लॉगिंग सुरू करा"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"बग रिपोर्टमध्ये अतिरिक्त डिव्हाइस-विशिष्ट विक्रेता लॉगचा समावेश करा ज्यामध्ये खाजगी माहिती असू शकते, अधिक बॅटरी आणि/किंवा अधिक स्टोरेज वापरले जाईल."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"विंडो ॲनिमेशन स्केल"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ट्रांझिशन ॲनिमेशन स्केल"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ॲनिमेटर कालावधी स्केल"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म आणि रिमाइंडर"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"या ॲपला अलार्म सेट करण्याची किंवा वेळेनुसार संवेदनशील असलेल्या कृती शेड्युल करण्याची अनुमती द्या. हे ॲपला बॅकग्राउंडमध्ये रन होऊ देते, ज्यामुळे जास्त बॅटरी वापरली जाऊ शकते.\n\nही परवानगी बंद असल्यास, सध्याचे अलार्म आणि या ॲपद्वारे शेड्युल केलेले वेळेवर आधारित इव्हेंट काम करणार नाहीत."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्युल, अलार्म, रिमाइंडर, घड्याळ"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"व्यत्यय आणू नका"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सुरू करा"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"व्यत्यय आणू नका सुरू करा"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"कधीही नाही"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"कालावधी"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक वेळी विचारा"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"तुम्ही बंद करेपर्यंत"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(नाव नाही)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"हा फोन"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"हा टॅबलेट"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 50f0c2e..3301b54 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -236,7 +236,7 @@
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Pilih profil"</string>
<string name="category_personal" msgid="6236798763159385225">"Peribadi"</string>
- <string name="category_work" msgid="4014193632325996115">"Tempat Kerja"</string>
+ <string name="category_work" msgid="4014193632325996115">"Kerja"</string>
<string name="category_private" msgid="4244892185452788977">"Persendirian"</string>
<string name="category_clone" msgid="1554511758987195974">"Klon"</string>
<string name="development_settings_title" msgid="140296922921597393">"Pilihan pembangun"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Benarkan pemuatan lapisan nyahpepijat GPU untuk apl penyahpepijatan"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Dayakan pengelogan vendor berjela"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Sertakan log tambahan vendor khusus peranti dalam laporan pepijat, yang mungkin mengandungi maklumat peribadi, menggunakan lebih banyak kuasa bateri dan/atau menggunakan lebih banyak storan."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Skala animasi tetingkap"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Skala animasi peralihan"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Skala tempoh juruanimasi"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Penggera & peringatan"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Benarkan apl ini menetapkan penggera dan menjadualkan tindakan yang sensitif masa. Hal ini membolehkan apl berjalan di latar, yang mungkin menggunakan lebih banyak bateri.\n\nJika kebenaran ini dimatikan, penggera sedia ada dan acara berdasarkan masa yang dijadualkan oleh apl ini tidak akan berfungsi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadual, penggera, peringatan, jam"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Jangan Ganggu"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Hidupkan"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Hidupkan Jangan Ganggu"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Jangan sekali-kali"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Tempoh"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Tanya setiap kali"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Sehingga anda matikan"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Tiada nama)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Sebentar tadi"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefon ini"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tablet ini"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index aa21f3c..79f1bc3 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"အမှားရှာအက်ပ်များအတွက် GPU အမှားရှာအလွှာများ ဖွင့်ခွင့်ပြုသည်"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"အကျယ်ရှင်းလင်းချက်ပံ့ပိုးသူ မှတ်တမ်းဖွင့်ရန်"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"ချွတ်ယွင်းမှု အစီရင်ခံချက်တွင် စက်ပစ္စည်းအလိုက် ထုတ်လုပ်သူမှတ်တမ်းများကို ထည့်သွင်းခြင်းဖြင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များ ပါဝင်ခြင်း၊ ဘက်ထရီပိုသုံးခြင်း နှင့်/သို့မဟုတ် သိုလှောင်ခန်းပိုသုံးခြင်းတို့ ဖြစ်စေနိုင်သည်။"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"လှုပ်ရှားသက်ဝင်ပုံစကေး"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"သက်ဝင်အသွင်ပြောင်းခြင်း"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"လှုပ်ရှားမှုကြာချိန်စကေး"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"နှိုးစက်နှင့် သတိပေးချက်များ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"နှိုးစက်သတ်မှတ်ရန်နှင့် အချိန်တိကျရန် လိုအပ်သည့် လုပ်ဆောင်ချက်များအတွက် အစီအစဉ်ဆွဲရန် ဤအက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက အက်ပ်ကို နောက်ခံတွင် လုပ်ဆောင်ခွင့်ပေးပြီး ဘက်ထရီပိုသုံးနိုင်သည်။\n\nဤခွင့်ပြုချက်ကို ပိတ်ထားပါက ဤအက်ပ်ဖြင့် အစီအစဉ်ဆွဲထားသော လက်ရှိနှိုးစက်နှင့် အချိန်သတ်မှတ်ထားသည့် အစီအစဉ်များ အလုပ်လုပ်တော့မည် မဟုတ်ပါ။"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"အချိန်ဇယား၊ နှိုးစက်၊ သတိပေးချက်၊ နာရီ"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"မနှောင့်ယှက်ရ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ဖွင့်ရန်"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'မနှောင့်ယှက်ရ\' ဖွင့်ခြင်း"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ဘယ်တော့မှ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ကြာချိန်"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"အမြဲမေးရန်"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"သင်ပိတ်လိုက်သည် အထိ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(အမည်မရှိ)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ဤဖုန်း"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ဤတက်ဘလက်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 1a672c39..23031c3 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Tillat GPU-feilsøkingslag for feilsøkingsapper"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Detaljert leverandørlogging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inkluder flere enhetsspesifikke leverandørlogger i feilrapporter, som kan inneholde privat informasjon, bruke mer batteri og/eller bruke mer lagringsplass."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Animasjonsskala for vindu"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Animasjonsskala for overgang"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Varighetsskala for animasjoner"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmer og påminnelser"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Gi denne appen tillatelse til å angi alarmer og planlegge tidssensitive handlinger. Dette gir appen tillatelse til å kjøre i bakgrunnen, noe som kan bruke mer batteri.\n\nHvis denne tillatelsen er av, fungerer ikke eksisterende alarmer og tidsbaserte hendelser som er planlagt av denne appen."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"tidsplan, alarm, påminnelse, klokke"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ikke forstyrr"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Slå på"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Slå på Ikke forstyrr"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Aldri"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Varighet"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spør hver gang"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Til du slår av"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Uten navn)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefonen"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Dette nettbrettet"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 1e33f88..38e137d 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -136,7 +136,7 @@
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD अडियो"</string>
<string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"हियरिङ डिभाइसहरू"</string>
<string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE अडियो"</string>
- <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"श्रवण यन्त्रहरूमा जडान गरियो"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"हियरिङ डिभाइसमा कनेक्ट गरियो"</string>
<string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE अडियोमा कनेक्ट गरिएको छ"</string>
<string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मिडिया अडियोसँग जडित"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन अडियोमा जडान गरियो"</string>
@@ -154,7 +154,7 @@
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"फोन अडियोको लागि प्रयोग गर्नुहोस्"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल ट्रान्सफरका लागि प्रयोग गर्नुहोस्"</string>
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुटको लागि प्रयोग गर्नुहोस्"</string>
- <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"श्रवण यन्त्रहरूका लागि प्रयोग गर्नुहोस्"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"हियरिङ डिभाइसका लागि प्रयोग गर्नुहोस्"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO मा प्रयोग गर्नुहोस्"</string>
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"कनेक्ट गर्नुहोस्"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"जोडी"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"डिबग एपका लागि GPU का डिबग लेयर लोड गर्नुहोस्"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"भर्बोज भेन्डर लगिङ अन गर्नुहोस्"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"बग रिपोर्टहरूमा डिभाइस विशेषका विक्रेताका अतिरिक्त लगहरू समावेश गर्नुहोस्। यी लगमा निजी जानकारी समावेश हुन सक्छन्, यिनले ब्याट्रीको खपत बढाउन र/वा थप भण्डारण प्रयोग गर्न सक्छन्।"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"विन्डो एनिमेसन स्केल"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"संक्रमण एनिमेसन स्केल"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"एनिमेसनको अवधि मापन"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म तथा रिमाइन्डर"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"बाधा नपुऱ्याउनुहोस्"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"अन गर्नुहोस्"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई अन गर्नुहोस्"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"कहिल्यै होइन"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"अवधि"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक पटक सोध्नुहोस्"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"तपाईंले अफ नगरेसम्म"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(कुनै नाम छैन)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"यो फोन"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"यो ट्याब्लेट"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 226f592..9e0088f 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Sta laden van GPU-foutopsporingslagen toe voor foutopsporingsapps"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Uitgebreide leverancierslogboeken aanzetten"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Neem aanvullende apparaatspecifieke leverancierslogboeken op in bugrapporten. Deze kunnen privégegevens bevatten, meer batterijlading gebruiken en/of meer opslagruimte gebruiken"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Vensteranimatieschaal"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Overgangsanimatieschaal"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Duur van animatieschaal"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en herinneringen"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Sta toe dat deze app wekkers zet en tijdgevoelige acties plant. De app kan hierdoor op de achtergrond worden uitgevoerd, waardoor je misschien meer batterijlading verbruikt.\n\nAls dit recht uitstaat, werken door deze app geplande bestaande wekkers en tijdgebaseerde afspraken niet."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plannen, schema, wekker, alarm, herinnering, klok"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Niet storen"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aanzetten"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zet Niet storen aan."</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nooit"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duur"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Altijd vragen"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Totdat je uitzet"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Geen naam)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Zojuist"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Deze telefoon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Deze tablet"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 562a558..8d4e5cc 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -125,7 +125,7 @@
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"ମିଡିଆ ଅଡିଓ"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"ଫୋନ କଲ"</string>
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍"</string>
- <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍ପୁଟ୍ ଡିଭାଇସ୍"</string>
+ <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନପୁଟ ଡିଭାଇସ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟରନେଟ ଆକ୍ସେସ"</string>
<string name="bluetooth_profile_pbap" msgid="2103406516858653017">"କଣ୍ଟାକ୍ଟ ଓ କଲ ଇତିହାସକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ସୂଚନାକୁ କଲ ଘୋଷଣା ଏବଂ ଆହୁରି ଅଧିକ ପାଇଁ ବ୍ୟବହାର କରାଯିବ"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ଡିବଗ୍ ଆପ୍ଗୁଡ଼ିକ ପାଇଁ GPU ଡିବଗ୍ ଲେୟର୍ ଲୋଡ୍ କରିବାର ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"ଭର୍ବୋସ ଭେଣ୍ଡର୍ ଲଗିଂ ସକ୍ଷମ କରନ୍ତୁ"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"ବଗ୍ ରିପୋର୍ଟଗୁଡ଼ିକରେ ଅତିରିକ୍ତ ଡିଭାଇସ୍-ନିର୍ଦ୍ଦିଷ୍ଟ ଲଗଗୁଡ଼ିକ ସାମିଲ କରନ୍ତୁ, ଯେଉଁଥିରେ ବ୍ୟକ୍ତିଗତ ସୂଚନା ଥାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ଏବଂ/କିମ୍ବା ଅଧିକ ଷ୍ଟୋରେଜ୍ ବ୍ୟବହାର କରିପାରେ।"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"ୱିଣ୍ଡୋ ଆନିମେସନ୍ ସ୍କେଲ୍"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ଟ୍ରାଞ୍ଜିସନ୍ ଆନିମେସନ୍ ସ୍କେଲ୍"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ଆନିମେଟର୍ ଅବଧି ସ୍କେଲ୍"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ଏହି ଆପକୁ ଆଲାରାମ୍ ସେଟ୍ କରିବାକୁ ଏବଂ ସମୟ-ସମ୍ବେଦନଶୀଳ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଏହା ଆପକୁ ପୃଷ୍ଠପଟରେ ଚାଲିବାକୁ ଦେଇଥାଏ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ।\n\nଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଅଛି, ତେବେ ଏହି ଆପ୍ ଦ୍ୱାରା ସିଡୁଲ୍ କରାଯାଇଥିବା ପୂର୍ବରୁ ଥିବା ଆଲାରାମ୍ ଏବଂ ସମୟ-ଆଧାରିତ ଇଭେଣ୍ଟଗୁଡ଼ିକ କାମ କରିବ ନାହିଁ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ସିଡୁଲ୍, ଆଲାରାମ୍, ରିମାଇଣ୍ଡର୍, ଘଣ୍ଟା"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ କରନ୍ତୁ"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"କଦାପି ନୁହେଁ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ଅବଧି"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(କୌଣସି ନାମ ନାହିଁ)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ଏହି ଫୋନ"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ଏହି ଟାବଲେଟ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f80ec8d..4091f9b 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -316,7 +316,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"ਬਲੂਟੁੱਥ ਆਡੀਓ LDAC ਕੋਡੇਕ: ਪਲੇਬੈਕ ਕੁਆਲਿਟੀ"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"ਬਲੂਟੁੱਥ ਆਡੀਓ LDAC\nਕੋਡੇਕ ਚੋਣ ਨੂੰ ਟ੍ਰਿਗਰ ਕਰੋ: ਪਲੇਬੈਕ ਕੁਆਲਿਟੀ"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"ਸਟ੍ਰੀਮਿੰਗ: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
- <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"ਨਿੱਜੀ DNS"</string>
+ <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"ਪ੍ਰਾਈਵੇਟ DNS"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"ਨਿੱਜੀ DNS ਮੋਡ ਚੁਣੋ"</string>
<string name="private_dns_mode_off" msgid="7065962499349997041">"ਬੰਦ"</string>
<string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"ਸਵੈਚਲਿਤ"</string>
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ਡੀਬੱਗ ਐਪਾਂ ਲਈ GPU ਡੀਬੱਗ ਤਹਿਆਂ ਨੂੰ ਲੋਡ ਹੋਣ ਦਿਓ"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"ਵਰਬੋਸ ਵਿਕਰੇਤਾ ਲੌਗਿੰਗ ਚਾਲੂ ਕਰੋ"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"ਬੱਗ ਰਿਪੋਰਟਾਂ ਵਿੱਚ ਵਧੀਕ ਡੀਵਾਈਸ ਨਾਲ ਸੰਬੰਧਿਤ ਵਿਕਰੇਤਾ ਲੌਗ ਸ਼ਾਮਲ ਕਰੋ, ਜਿਨ੍ਹਾਂ ਵਿੱਚ ਨਿੱਜੀ ਜਾਣਕਾਰੀ, ਬੈਟਰੀ ਦੀ ਵਧੇਰੇ ਵਰਤੋਂ, ਅਤੇ/ਜਾਂ ਵਧੇਰੀ ਸਟੋਰੇਜ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀ ਹੈ।"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"ਵਿੰਡੋ ਐਨੀਮੇਸ਼ਨ ਸਕੇਲ"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ਟ੍ਰਾਂਜ਼ਿਸ਼ਨ ਐਨੀਮੇਸ਼ਨ ਸਕੇਲ"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"ਐਨੀਮੇਟਰ ਮਿਆਦ ਸਕੇਲ"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਵੱਧ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਇਸ ਐਪ ਰਾਹੀਂ ਨਿਯਤ ਕੀਤੇ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ਸਮਾਂ-ਸੂਚੀ, ਅਲਾਰਮ, ਰਿਮਾਈਂਡਰ, ਘੜੀ"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ਚਾਲੂ ਕਰੋ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ਕਦੇ ਵੀ ਨਹੀਂ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ਮਿਆਦ"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(ਕੋਈ ਨਾਮ ਨਹੀਂ)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ਇਹ ਟੈਬਲੈੱਟ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 8babb8b..ef09687 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Zezwalaj na ładowanie warstw debugowania GPU"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Włącz szczegółowe rejestrowanie dostawcy"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Dołączaj do raportów o błędach dodatkowe dane dostawcy dotyczące konkretnego urządzenia, które mogą zawierać dane prywatne oraz wykorzystywać więcej baterii lub pamięci"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Skala animacji okna"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Skala animacji przejścia"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Skala długości animacji"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmy i przypomnienia"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Zezwalaj tej aplikacji na ustawianie alarmów i planowanie działań, w przypadku których czas jest istotny. Aplikacja będzie mogła działać w tle, co może zwiększyć wykorzystanie baterii.\n\nJeśli nie włączysz tego uprawnienia, istniejące alarmy i zaplanowane wydarzenia z tej aplikacji nie będą działać."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"harmonogram, alarm, przypomnienie, zegar"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Nie przeszkadzać"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Włącz"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Włącz tryb Nie przeszkadzać"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nigdy"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Czas"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Zawsze pytaj"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Dopóki nie wyłączysz"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Bez nazwy)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ten telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ten tablet"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 337b011..e441f05 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permitir carregamento de camadas de depuração de GPU p/ apps de depuração"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ativar registro detalhado de fornecedor"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Incluir mais registros de fornecedores específicos do dispositivo em relatórios de bugs. Isso pode aumentar o uso da bateria e/ou do armazenamento, e os relatórios podem conter informações particulares."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação da janela"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração do Animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Não perturbe"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nunca"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duração"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sem nome)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index a40b95f..7aed3f4 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite carregamento de camadas de depuração de GPU para apps de depuração"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ativ. registo do fornecedor"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Inclua registos adicionais de fornecedores específicos de dispositivos em relatórios de erros, que podem conter informações privadas, utilizar mais bateria e/ou utilizar mais armazenamento."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação de transição"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração de animação"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permita que a app defina alarmes e agende ações com um horário específico. Esta ação permite que a app seja executada em segundo plano, o que pode usar mais bateria.\n\nSe esta autorização estiver desativada, os alarmes existentes e os eventos com base no tempo agendados por esta app não funcionam."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"agendar, alarme, lembrete, relógio"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Não incomodar"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o modo Não incomodar"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nunca"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duração"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sem nome)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telemóvel"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 337b011..e441f05 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permitir carregamento de camadas de depuração de GPU p/ apps de depuração"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ativar registro detalhado de fornecedor"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Incluir mais registros de fornecedores específicos do dispositivo em relatórios de bugs. Isso pode aumentar o uso da bateria e/ou do armazenamento, e os relatórios podem conter informações particulares."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Escala de animação da janela"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Escala de animação de transição"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Escala de duração do Animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Não perturbe"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nunca"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duração"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Sem nome)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telefone"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Este tablet"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4bddeb9a..9a79bdf 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Permite încărcarea nivelurilor de depanare GPU pentru aplicațiile de depanare"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Activează înregistrarea detaliată a furnizorilor"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Include alte jurnale ale furnizorilor de dispozitive în rapoartele de eroare, care pot conține informații private, folosește mai multă baterie și/sau mai mult spațiu de stocare."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Scară animație fereastră"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Scară tranziție animații"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Scară durată Animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarme și mementouri"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite acestei aplicații să seteze alarme și să planifice acțiuni care trebuie realizate în timp scurt. Astfel, aplicația poate să ruleze în fundal, ceea ce ar putea crește consumul de baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programare, alarmă, memento, ceas"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Nu deranja"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activează"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activează Nu deranja"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Niciodată"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Durată"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Întreabă de fiecare dată"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivezi"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Fără nume)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Acest telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Această tabletă"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index f30a125..b768cd9 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Включить загрузку слоев отладки графического процессора"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Подробный журнал поставщика"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Включать в информацию об ошибках дополнительные записи поставщика об устройстве, которые могут содержать личные данные и занимать больше места. Также это может увеличить расход заряда батареи."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Анимация окон"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Анимация переходов"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Длительность анимации"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники и напоминания"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Вы можете разрешить этому приложению устанавливать будильники и планировать запуск действий в определенное время. В этом случае оно будет работать в фоновом режиме и быстрее расходовать заряд батареи.\n\nЕсли отключить это разрешение, текущие будильники и созданные приложением события перестанут запускаться."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"установить, будильник, напоминание, часы"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не беспокоить"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включить"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включите режим \"Не беспокоить\""</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Никогда"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Длительность"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Всегда спрашивать"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Пока вы не отключите"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"Имя не указано"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Этот смартфон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Этот планшет"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 9f1aca6..e6095e8 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"නිදොසීමේ යෙදුම්වලට GPU නිදොසීමේ ස්තර පූරණයට ඉඩ දෙ."</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"verbose vendor පිරීම සබල කරන්න"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"පුද්ගලික තොරතුරු අන්තර්ගත විය හැකි, වැඩි බැටරි බලයක් භාවිත කිරීමට සහ/හෝ වැඩි ගබඩා ඉඩක් භාවිත කිරීමට හැකි අමතර උපාංග නිශ්චිත විකුණුම්කරු ලොග, දෝෂ වාර්තාවල අඩංගු වේ."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"කවුළු සජීවිකරණ පරිමාණය"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"සංක්රමණ සජීවන පරිමාණය"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"සජීවක කාල පරාස පරිමාණය"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"එලාම සහ සිහිකැඳවීම්"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"එලාම සැකසීමට සහ කාල සංවේදී ක්රියා කාලසටහන්ගත කිරීමට මෙම යෙදුමට ඉඩ දෙන්න. මෙය පසුබිමේ ධාවනය වීමට යෙදුමට ඉඩ දෙයි, එය වැඩි බැටරිය වැඩියෙන් භාවිත කළ හැකිය.\n\nමෙම අවසරය ක්රියාවිරහිත නම්, මෙම යෙදුම මඟින් සැලසුම් කර ඇති තිබෙන එලාම සහ වේලාව පදනම් කර ගත් සිදුවීම් ක්රියා නොකරනු ඇත."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"කාල සටහන, එලාමය, සිහිකැඳවීම, ඔරලෝසුව"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"බාධා නොකරන්න"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ක්රියාත්මක කරන්න"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"බාධා නොකරන්න ක්රියාත්මක කරන්න"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"කිසි විටක නැත"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"කාල සීමාව"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"සෑම විටම ඉල්ලන්න"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"ඔබ ක්රියාවිරහිත කරන තුරු"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(නමක් නොමැත)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"මේ දැන්"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"මෙම දුරකථනය"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"මෙම ටැබ්ලටය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index db8301a..bc09bdf 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Povoliť načítanie vrstiev ladenia grafického procesora na ladenie aplikácií"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Aktivovať podrobný zápis dodávateľov do denníka"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Zahŕňať do hlásení chýb ďalšie denníky dodávateľa pre konkrétne zariadenie, ktoré môžu obsahovať osobné údaje, zvýšiť spotrebu batérie alebo zabrať viac ukladacieho priestoru"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Mierka animácie okna"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Mierka animácie premeny"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Mierka dĺžky animácie"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Budíky a pripomenutia"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Povoľte tejto aplikácii nastavovať budíky a plánovať akcie s časovým obmedzením. Aplikácii to umožní pracovať na pozadí, čo môže zvýšiť spotrebu batérie.\n\nAk je toto povolenie vypnuté, existujúce budíky a udalosti s časovým obmedzením naplánované touto aplikáciou nebudú fungovať."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plán, budík, pripomenutie, hodiny"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Režim bez vyrušení"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Zapnúť"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zapnite režim bez vyrušení"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nikdy"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trvanie"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vždy sa opýtať"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Bez mena)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefón"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Tento tablet"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 5460c3b..7ce5d46 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Aplikacijam za odpravljanje napak dovoli nalaganje slojev za odpravljanje napak GPE."</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Omogoči podrobno beleženje za ponudnika"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Vključi dodatne dnevnike ponudnika, odvisne od posamezne naprave, v poročila o napakah. Takšno poročilo lahko vsebuje zasebne podatke, porabi več energije baterije in/ali več shrambe."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Merilo animacije okna"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Merilo animacije prehoda"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Merilo trajanja animacije"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi in opomniki"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tej aplikaciji dovolite nastavljanje alarmov in načrtovanje časovno občutljivih dejanj. S tem aplikaciji omogočite izvajanje v ozadju, kar bo morda povečalo porabo energije baterije.\n\nČe je to dovoljenje izklopljeno, obstoječi alarmi in časovno občutljivi dogodki, ki jih nastavi ta aplikacija, ne bodo delovali."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"načrtovanje, urnik, alarm, opomnik, ura"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne moti"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Vklopi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Vklop načina »Ne moti«"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Nikoli"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Trajanje"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vedno vprašaj"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Ni imena)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ta telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ta tablični računalnik"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 3db7146..313fbc2 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Lejo ngarkimin e shtresave të korrigjimit të GPU-së për aplikacionet e korrigjimit"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Aktivizo evidencat e tregtuesit me shumë fjalë"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Përfshi evidenca shtesë të treguesve specifike për pajisjen në raportet e defekteve, që mund të përfshijnë informacion privat, mund të përdorin më shumë bateri dhe/ose të përdorin më shumë hapësirë ruajtëse."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Animacioni i dritares"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Animacioni kalimtar"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Kohëzgjatja e animatorit"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmet dhe alarmet rikujtuese"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lejo që ky aplikacion të caktojë alarmet dhe të planifikojë veprime që kanë një afat të caktuar. Kjo mundëson që aplikacioni të ekzekutohet në sfond, gjë që mund të përdorë më shumë bateri.\n\nNëse kjo leje është caktuar si joaktive, alarmet ekzistuese dhe ngjarjet në bazë kohore të planifikuara nga ky aplikacion nuk do të funksionojnë."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planifiko, alarm, alarm rikujtues, ora"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Mos shqetëso"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivizo"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivizo \"Mos shqetëso\""</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Asnjëherë"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Kohëzgjatja"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pyet çdo herë"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Derisa ta çaktivizosh"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Pa emër)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ky tablet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index fa94c2e..217da2c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Учитава отклањање грешака GPU-a у апл. за отклањање грешака"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Опширне евиденције продавца"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Уврштава у извештаје о грешкама додатне посебне евиденције продавца за уређаје, које могу да садрже приватне податке, да троше више батерије и/или да користе више меморије."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Размера анимације прозора"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Размера анимације прелаза"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Аниматорова размера трајања"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Аларми и подсетници"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Омогућите овој апликацији да подешава аларме и заказује временски осетљиве радње. То омогућава да апликација буде покренута у позадини, што може да троши више батерије.\n\nАко је ова дозвола искључена, постојећи аларми и догађаји засновани на времену заказани помоћу ове апликације неће радити."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"заказати, аларм, подсетник, сат"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не узнемиравај"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Укључи"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Укључите режим Не узнемиравај"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Никад"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Трајање"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Питај сваки пут"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Док не искључите"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Без имена)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Управо"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овај телефон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Овај таблет"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 636d969..38faa8c 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Tillåt att felsökningsappar läser in GPU-felsökningslager"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Aktivera utförlig loggning för leverantörer"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Ta med ytterligare enhetsspecifika leverantörsloggar i felrapporter. Dessa kan innehålla privata uppgifter samt använda mer batteri och/eller mer lagringsutrymme."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Skala – fönsteranimering"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Skala – övergångsanimering"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Längdskala för Animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm och påminnelser"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tillåt att den här appen ställer in alarm och schemalägger tidskänsliga åtgärder. Om du tillåter detta kan appen köras i bakgrunden, vilket kan dra mer batteri.\n\nOm behörigheten är inaktiverad fungerar inte befintliga alarm och tidsbaserade händelser som schemalagts av den här appen."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schema, alarm, påminnelse, klocka"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Stör ej"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivera"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivera Stör ej."</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Aldrig"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Varaktighet"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Fråga varje gång"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Tills du inaktiverar funktionen"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Inget namn)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Den här telefonen"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Den här surfplattan"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 01b786d..744a79e 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Ruhusu upakiaji wa safu za utatuzi wa GPU za programu za utatuzi"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Washa uwekaji kumbukumbu za muuzaji kwa kutumia sauti"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Jumuisha kumbukumbu zaidi za muuzaji ambazo ni mahususi kwa kifaa kwenye ripoti za hitilafu, ambazo huenda zikawa na maelezo ya faragha, zikatumia chaji nyingi ya betri na/au zikatumia nafasi kubwa ya hifadhi."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Uhuishaji kwenye dirisha"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Mageuzi ya kipimo cha uhuishaji"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Mizani ya muda wa uhuishaji"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Kengele na vikumbusho"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Ruhusu programu hii iweke kengele na ratiba za vitendo vingine vinavyotegemea wakati. Hatua hii inairuhusu programu itumike chinichini, hali inayoweza kutumia chaji nyingi ya betri.\n\nIkiwa ruhusa hii itazimwa, kengele zilizopo na ratiba za vitendo vinavyotegemea wakati zilizowekwa na programu hii hazitafanya kazi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ratiba, kengele, kikumbusho, saa"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Usinisumbue"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Washa"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Washa kipengele cha Usinisumbue"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Kamwe usiwashe"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Muda"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Uliza kila wakati"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Hadi utakapoizima"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Hakuna jina)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Sasa hivi"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Simu hii"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Kishikwambi hiki"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 8aaeb5d..113a139 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"பிழைத்திருத்த ஆப்ஸிற்கு, GPU பிழைத்திருத்த லேயர்களை ஏற்றுவதற்கு அனுமதி"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"வெர்போஸ் வெண்டார் பதிவை இயக்கு"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"பிழை அறிக்கைகளில் சாதனம் சார்ந்த கூடுதல் வெண்டார் பதிவுகளைச் சேர்க்கவும். அவற்றில் தனிப்பட்ட தகவல்கள், அதிக பேட்டரி உபயோகம் மற்றும்/அல்லது அதிக சேமிப்பிட உபயோகம் குறித்த தகவல்கள் இருக்கக்கூடும்."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"சாளர அனிமேஷன் வேகம்"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"அனிமேஷன் மாற்றத்தின் வேகம்"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"அனிமேட்டர் கால அளவு"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"அலாரங்கள் & நினைவூட்டல்கள்"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"அலாரங்களை அமைக்கவும் குறிப்பிட்ட கால இடைவெளியில் செயல்களைத் திட்டமிடவும் இந்த ஆப்ஸை அனுமதிக்கும். இது ஆப்ஸ் பின்னணியில் இயங்குவதை அனுமதிக்கும், இதற்காக அதிக பேட்டரியைப் பயன்படுத்தக்கூடும்.\n\nஇந்த அனுமதி முடக்கப்பட்டிருந்தால் இந்த ஆப்ஸ் மூலம் திட்டமிடப்பட்ட ஏற்கெனவே அமைத்த அலாரங்களும் நேர அடிப்படையிலான நிகழ்வுகளும் வேலை செய்யாது."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"திட்டமிடல், அலாரம், நினைவூட்டல், கடிகாரம்"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"தொந்தரவு செய்யாதே"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ஆன் செய்"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"தொந்தரவு செய்ய வேண்டாம் என்பதை ஆன் செய்யும்"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ஒருபோதும் வேண்டாம்"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"கால அளவு"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ஒவ்வொரு முறையும் கேள்"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"ஆஃப் செய்யும் வரை"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(பெயர் இல்லை)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"இந்த மொபைல்"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"இந்த டேப்லெட்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 16d40c2..83f2faa 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"డీబగ్ యాప్ల కోసం GPU డీబగ్ లేయర్లను లోడ్ చేయడాన్ని అనుమతించండి"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"వివరణాత్మక వెండార్ లాగింగ్ను ఎనేబుల్ చేయండి"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"బగ్ రిపోర్ట్లలో అదనపు పరికర-నిర్దిష్ట వెండార్ లాగ్లను చేర్చండి, అవి ప్రైవేట్ సమాచారాన్ని కలిగి ఉండవచ్చు, మరింత బ్యాటరీని, మరియు/లేదా మరింత స్టోరేజ్ను ఉపయోగించవచ్చు."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"విండో యానిమేషన్ స్కేల్"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ట్రాన్సిషన్ యానిమేషన్ స్కేల్"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"యానిమేటర్ వ్యవధి స్కేల్"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"అలారాలు & రిమైండర్లు"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"అలారాలను సెట్ చేయడానికి, టైమ్-సెన్సిటివ్ చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. ఇది యాప్ను బ్యాక్గ్రౌండ్లో రన్ అవడానికి అనుమతిస్తుంది, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు.\n\nఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ ద్వారా షెడ్యూల్ చేసినటువంటి ఇప్పటికే ఉన్న అలారాలు, టైమ్-ఆధారిత ఈవెంట్లు పనిచేయవు."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"అంతరాయం కలిగించవద్దు"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ఆన్ చేయండి"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"అంతరాయం కలిగించవద్దును ఆన్ చేయండి"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ఎప్పటికీ వద్దు"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"వ్యవధి"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ప్రతిసారి అడగాలి"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"మీరు ఆఫ్ చేసే వరకు"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(పేరు లేదు)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"ఈ ఫోన్"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"ఈ టాబ్లెట్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 014ba26..ef31592 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"อนุญาตให้โหลดเลเยอร์การแก้ไขข้อบกพร่อง GPU สำหรับแอปแก้ไขข้อบกพร่อง"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"เปิดการบันทึกเวนเดอร์แบบละเอียด"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"รวมบันทึกเวนเดอร์เพิ่มเติมเฉพาะอุปกรณ์ไว้ในรายงานข้อบกพร่อง ซึ่งอาจมีข้อมูลส่วนตัว ใช้แบตเตอรี่มากขึ้น และ/หรือใช้พื้นที่เก็บข้อมูลมากขึ้น"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"อัตราการเคลื่อนไหวของหน้าต่าง"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"อัตราการเคลื่อนไหวของการเปลี่ยนภาพ"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"อัตราความเร็วตามตัวสร้างภาพเคลื่อนไหว"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"การปลุกและการช่วยเตือน"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"อนุญาตให้แอปนี้ตั้งปลุกและกำหนดเวลาการดำเนินการที่ต้องคำนึงถึงเวลาเป็นสำคัญ สิทธิ์นี้ช่วยให้แอปทำงานในเบื้องหลังได้ จึงอาจทำให้ใช้แบตเตอรี่มากขึ้น\n\nหากปิดใช้สิทธิ์นี้ การปลุกที่มีอยู่และกิจกรรมที่ต้องคำนึงถึงเวลาเป็นสำคัญซึ่งแอปนี้กำหนดเวลาไว้จะไม่ทำงาน"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"กำหนดเวลา การปลุก การช่วยเตือน นาฬิกา"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ห้ามรบกวน"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"เปิด"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"เปิด \"ห้ามรบกวน\""</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"ไม่เลย"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"ระยะเวลา"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ถามทุกครั้ง"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"จนกว่าคุณจะปิด"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(ไม่มีชื่อ)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"เมื่อสักครู่"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"แท็บเล็ตเครื่องนี้"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 936f521..90acd6fa 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Payagang i-load ang GPU debug layer sa debug app"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"I-enable ang verbose vendor logging"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Magsama sa mga ulat ng bug ng mga karagdagang log ng vendor na partikular sa device, na posibleng may pribadong impormasyon, gumamit ng mas maraming baterya, at/o gumamit ng mas malaking storage."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Scale ng window animation"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Scale ng transition animation"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Scale ng tagal ng animator"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Mga alarm at paalala"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Payagan ang app na ito na magtakda ng mga alarm at mag-iskedyul ng mga pagkilos na may limitadong oras. Papayagan nitong tumakbo ang app sa background, na posibleng gumamit ng mas maraming baterya.\n\nKung naka-off ang pahintulot na ito, hindi gagana ang mga kasalukuyang alarm at event na nakabatay sa oras na naiskedyul ng app na ito."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"iskedyul, alarm, paalala, orasan"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Huwag Istorbohin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"I-on"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"I-on ang Huwag Istorbohin"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Hindi kailanman"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Tagal"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Magtanong palagi"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Hanggang sa i-off mo"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Walang pangalan)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Ngayon lang"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ang teleponong ito"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Ang tablet na ito"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index e8bac31..a30c3ee 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Hata ayıklama uygulamaları için GPU hata ayıklama katmanlarının yüklenmesine izin ver"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Ayrıntılı satıcı günlüğünü etkinleştir"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Hata raporlarına cihaza özgü ek satıcı günlükleri ekle. Bu günlükler gizli bilgiler içerebilir, daha fazla pil ve/veya daha fazla depolama alanı kullanabilir."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Pencere animasyonu ölçeği"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Geçiş animasyonu ölçeği"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animatör süre ölçeği"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmlar ve hatırlatıcılar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu uygulamanın alarm kurmasına ve zamana bağlı işlemler programlamasına izin verin. Bu izin, uygulamanın arka planda çalışmasına olanak sağlayarak daha fazla pil harcanmasına neden olabilir.\n\nBu izin verilmezse bu uygulama tarafından programlanmış mevcut alarmlar ve zamana bağlı etkinlikler çalışmaz."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"program, alarm, hatırlatıcı, saat"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Rahatsız Etmeyin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aç"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Rahatsız Etmeyin\'i açın"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Hiçbir zaman"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Süre"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Her zaman sor"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Siz kapatana kadar"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Adsız)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Bu tablet"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index deb0a40..c94e745 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Дозволити завантажувати шари налагодження ГП для додатків налагодження"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Увімкнути докладний журнал постачальника"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Включати у звіти про помилки додаткові записи постачальника про пристрій, які можуть містити особисті дані, призводити до надмірного споживання заряду акумулятора та/або використовувати більший обсяг пам\'яті."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Анімація вікон"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Анімація переходів"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Тривалість анімації"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники й нагадування"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дозволити цьому додатку налаштовувати будильники й створювати розклад дій. Додаток зможе працювати у фоновому режимі й використовувати більше заряду акумулятора.\n\nЯкщо вимкнути такий дозвіл, наявні будильники й дії, створені цим додатком, не працюватимуть."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"запланувати, будильник, нагадування, годинник"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не турбувати"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Увімкнути"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Увімкнути режим \"Не турбувати\""</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Ніколи"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Тривалість"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Запитувати щоразу"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Без імені)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Цей телефон"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Цей планшет"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 3ac4d95..3d2c89c 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"ڈیبگ ایپس کیلئے GPU ڈیبگ پرتوں کو لوڈ کرنے دیں"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"وربوس وینڈر لاگنگ فعال کریں"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"اضافی آلہ کے مخصوص وینڈر لاگز کو بگ رپورٹس میں شامل کریں، جن میں نجی معلومات، بیٹری کا زیادہ استعمال اور/یا اسٹوریج کا زیادہ استعمال شامل ہوسکتے ہیں۔"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"ونڈو اینیمیشن اسکیل"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"ٹرانزیشن اینیمیشن اسکیل"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"اینیمیٹر دورانیے کا اسکیل"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"الارمز اور یاد دہانیاں"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"اس ایپ کو الارمز سیٹ کرنے اور وقت کے لحاظ سے حساس کارروائیوں کو شیڈول کرنے کی اجازت دیں۔ اس سے ایپ کو پس منظر میں چلنے کی اجازت ملتی ہے، جس میں زیادہ بیٹری استعمال ہو سکتی ہے۔\n\n اگر یہ اجازت آف ہے تو موجودہ الارمز اور اس ایپ کے ذریعے شیڈول کردہ وقت پر مبنی ایونٹس کام نہیں کریں گے۔"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"شیڈول، الارم، یاد دہانی، گھڑی"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"ڈسٹرب نہ کریں"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"آن کریں"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ڈسٹرب نہ کریں\' کو آن کریں"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"کبھی نہیں"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"مدت"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ہر بار پوچھیں"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"یہاں تک کہ آپ آف کر دیں"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(کوئی نام نہیں)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"یہ فون"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"یہ ٹیبلیٹ"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index f285086..93ffac8 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Grafik protsessorni tuzatish qatlamlarini yuklashni yoqish"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Taʼminotchining batafsil jurnali"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Xatoliklar hisobotiga shaxsiy maʼlumotlari bor va koʻp joy olishi mumkin boʻlgan qurilma haqida taʼminotchining qoʻshimcha yozuvlari kiradi. Bunda batareya quvvati tezroq sarflanishi mumkin."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Oynalar animatsiyasi"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"O‘tish animatsiyasi"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animatsiya tezligi"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signal va eslatmalar"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu ilovaga signal oʻrnatish va vaqtga asoslangan amallarni rejalashtirishga ruxsat berish. Bunda ilovaga orqa fonda ishlashiga imkon beriladi, shu sababli batareya ortiqcha sarflanishi mumkin.\n\nAgar bu ruxsat oʻchirilsa, ushbu ilova tomonidan rejalashtirilgan mavjud signallar va vaqtga asoslangan tadbirlar ishlamaydi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"reja, signal, eslatma, soat"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Bezovta qilinmasin"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Yoqish"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Bezovta qilinmasin rejimini yoqing"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Hech qachon"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Davomiyligi"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Har safar so‘ralsin"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Rejimdan chiqilgunicha"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Ismi koʻrsatilmagan)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Hozir"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Shu telefon"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Shu planshet"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 9bb4ef5..414ad37 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Cho phép tải lớp gỡ lỗi GPU cho ứng dụng gỡ lỗi"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Bật tùy chọn ghi nhật ký chi tiết của nhà cung cấp"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Đưa thêm nhật ký của nhà cung cấp dành riêng cho thiết bị vào các báo cáo lỗi. Nhật ký này có thể chứa thông tin cá nhân, dùng nhiều pin hơn và/hoặc chiếm nhiều dung lượng lưu trữ hơn."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Tỷ lệ hình động của cửa sổ"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Tỷ lệ hình động chuyển tiếp"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Tỷ lệ thời lượng của trình tạo hình động"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Chuông báo và lời nhắc"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Cho phép ứng dụng này đặt chuông báo và lên lịch các hành động cần chính xác về thời gian. Tùy chọn này cho phép ứng dụng chạy ở chế độ nền và có thể làm tiêu hao nhiều pin.\n\nNếu không cấp quyền này, các chuông báo và sự kiện theo thời gian do ứng dụng này lên lịch sẽ không hoạt động."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"lịch biểu, chuông báo, lời nhắc, đồng hồ"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Không làm phiền"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Bật"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Bật chế độ Không làm phiền"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Không bao giờ"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Thời lượng"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Luôn hỏi"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Cho đến khi bạn tắt"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Không có tên)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Vừa xong"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Điện thoại này"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Máy tính bảng này"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index c1d5481..c0a8f2d 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"允许为调试应用加载 GPU 调试层"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"启用详细供应商日志记录"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"在 bug 报告中包含其他设备特定的供应商日志,这些日志可能会含有隐私信息、消耗更多电量和/或使用更多存储空间。"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"窗口动画缩放"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"过渡动画缩放"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator 时长缩放"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"闹钟和提醒"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。这项权限开启后,该应用将在后台运行,可能会消耗更多电池电量。\n\n如果您关闭此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"勿扰模式"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"永不"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"持续时间"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都询问"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(无姓名)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"这部手机"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"这部平板电脑"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index d09a281..74f2678 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"允許為偵錯應用程式載入 GPU 偵錯圖層"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"啟用詳細供應商記錄"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"在錯誤報告中加入其他裝置專屬供應商記錄,其中可能包含私人資料,並會耗用更多電量及/或儲存空間。"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"視窗動畫比例"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"轉場動畫比例"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Animator 片長比例"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"鬧鐘和提醒"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許此應用程式設定鬧鐘及安排具時效性的操作。這讓應用程式在背景中執行,因此可能會較耗電。\n\n如果關閉此權限,此應用程式將不會在預定時間響起已設定的鬧鐘,亦不會就特定時間的活動傳送通知。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"日程表, 鬧鐘, 提醒, 時鐘"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"請勿騷擾"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"開啟"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"開啟「請勿騷擾」模式"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"永不"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"持續時間"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"直至你關閉為止"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(沒有名稱)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"此平板電腦"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index fdb4cf3..5b068e3 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"允許載入 GPU 偵錯圖層為應用程式偵錯"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"啟用詳細供應商記錄功能"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"在錯誤報告中附上其他的裝置專屬供應商記錄。如果你這麼做,可能會增加電池用量及/或占用較多儲存空間,報告中可能也會包含私人資訊。"</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"視窗動畫比例"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"轉場動畫比例"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"動畫影片長度比例"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"鬧鐘和提醒"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許這個應用程式設定鬧鐘及安排有時效性的動作。之後應用程式可以在背景執行,並可能耗用較多電量。\n\n如果關閉這項權限,這個應用程式設定的現有鬧鐘將不會響起,系統也無法在預定的時間發出活動提醒。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"時間表, 鬧鐘, 提醒, 時鐘"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"零打擾"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"開啟"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"開啟「零打擾」模式"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"永不"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"時間長度"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"直到你關閉為止"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(無顯示名稱)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"這支手機"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"這台平板電腦"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 0a0fdff..da152ce 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -408,6 +408,18 @@
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Vumela izendlalelo zokususa amaphutha ze-GPU ngezinhlelo zokusebenza zokususa amaphutha"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Nika amandla ilogu yomthengisi we-verbose"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Faka phakathi amalogu athize womthengisi wedivayisi angeziwe, angase afake phakathi ulwazi oluyimfihlo, kusebenzisa ibhethri eningi, futhi/noma kusebenzisa isitoreji esiningi."</string>
+ <!-- no translation found for enable_verbose_vendor_logging_checkbox (3864578373293835530) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_title (6811217272559843592) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_summary (5226524769774370942) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_notification_action (1190831050259046071) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_will_disable (6175431593394522553) -->
+ <skip />
+ <!-- no translation found for verbose_vendor_logging_preference_summary_on (9017757242481762036) -->
+ <skip />
<string name="window_animation_scale_title" msgid="5236381298376812508">"Iwindi yesilinganisi sesithombe esinyakazayo"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Isilinganiso sesithombe soku"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Isilinganiso sobude besikhathi somenzi womfanekiso onyakazayo"</string>
@@ -557,6 +569,7 @@
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ama-alamu nezikhumbuzi"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Vumela le app isethe ama-alamu futhi ushejule izenzo zesikhathi esizwelayo. Lokhu kuvumela i-app iqhubeke ngemuva okungasebenzisa ibhethri lakho eliningi.\n\nUma le mvume ivaliwe, ama-alamu asele nemicimbi esekelwe esikhathini ehlelwe yile app ngeke kusebenze."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ishejuli, i-alamu, isikhumbuzi, iwashi"</string>
+ <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ungaphazamisi"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Vula"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Vula ukungaphazamisi"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Soze"</string>
@@ -569,6 +582,7 @@
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Ubude besikhathi"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Buza njalo"</string>
<string name="zen_mode_forever" msgid="3339224497605461291">"Uze uvale isikrini"</string>
+ <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(Alikho igama)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Le foni"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Le thebhulethi"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index d373201..27c386e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1361,6 +1361,9 @@
<!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] -->
<string name="keywords_alarms_and_reminders">schedule, alarm, reminder, clock</string>
+ <!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]-->
+ <string name="zen_mode_settings_title">Do Not Disturb</string>
+
<!-- Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] -->
<string name="zen_mode_enable_dialog_turn_on">Turn on</string>
<!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
@@ -1387,6 +1390,9 @@
<!-- Do not disturb: Duration option to always have DND on until it is manually turned off [CHAR LIMIT=60] -->
<string name="zen_mode_forever">Until you turn off</string>
+ <!-- [CHAR LIMIT=50] Zen mode settings: placeholder for a Contact name when the name is empty -->
+ <string name="zen_mode_starred_contacts_empty_name">(No name)</string>
+
<!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
<string name="time_unit_just_now">Just now</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 1597a4b..fc163ce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -430,13 +430,21 @@
return null;
}
+ /**
+ * Retrieves the user ID of a managed profile associated with a specific user.
+ *
+ * <p>This method iterates over the users in the profile group associated with the given user ID
+ * and returns the ID of the user that is identified as a managed profile user.
+ * If no managed profile is found, it returns {@link UserHandle#USER_NULL}.
+ *
+ * @param context The context used to obtain the {@link UserManager} system service.
+ * @param userId The ID of the user for whom to find the managed profile.
+ * @return The user ID of the managed profile, or {@link UserHandle#USER_NULL} if none exists.
+ */
private static int getManagedProfileId(Context context, int userId) {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
List<UserInfo> userProfiles = um.getProfiles(userId);
for (UserInfo uInfo : userProfiles) {
- if (uInfo.id == userId) {
- continue;
- }
if (uInfo.isManagedProfile()) {
return uInfo.id;
}
@@ -821,11 +829,11 @@
}
EnforcedAdmin admin =
RestrictedLockUtils.getProfileOrDeviceOwner(
- context, UserHandle.of(UserHandle.USER_SYSTEM));
+ context, context.getUser());
if (admin != null) {
return admin;
}
- int profileId = getManagedProfileId(context, UserHandle.USER_SYSTEM);
+ int profileId = getManagedProfileId(context, context.getUserId());
return RestrictedLockUtils.getProfileOrDeviceOwner(context, UserHandle.of(profileId));
}
@@ -848,7 +856,7 @@
if (admin != null) {
return admin;
}
- int profileId = getManagedProfileId(context, UserHandle.USER_SYSTEM);
+ int profileId = getManagedProfileId(context, context.getUserId());
return RestrictedLockUtils.getProfileOrDeviceOwner(context, UserHandle.of(profileId));
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
index 7669e79b..f8c3a93 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
@@ -1,9 +1,4 @@
# Default reviewers for this and subdirectories.
-siyuanh@google.com
-hughchen@google.com
-timhypeng@google.com
-robertluo@google.com
-songferngwang@google.com
yqian@google.com
chelseahao@google.com
yiyishen@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index c5e86b4..4f2329b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -327,4 +327,12 @@
return sInstance;
}
}
+
+ /** Testing only. Reset the instance to avoid tests affecting each other. */
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ public static void resetInstance() {
+ synchronized (PowerAllowlistBackend.class) {
+ sInstance = null;
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index b7758de..4e1d8e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -671,7 +671,7 @@
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
@SuppressWarnings("NewApi")
@VisibleForTesting
- void addMediaDevice(MediaRoute2Info route, RoutingSessionInfo activeSession) {
+ void addMediaDevice(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo activeSession) {
final int deviceType = route.getType();
MediaDevice mediaDevice = null;
switch (deviceType) {
@@ -711,8 +711,13 @@
case TYPE_HEARING_AID:
case TYPE_BLUETOOTH_A2DP:
case TYPE_BLE_HEADSET:
+ if (route.getAddress() == null) {
+ Log.e(TAG, "Ignoring bluetooth route with no set address: " + route);
+ break;
+ }
final BluetoothDevice device =
- BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
+ BluetoothAdapter.getDefaultAdapter()
+ .getRemoteDevice(route.getAddress());
final CachedBluetoothDevice cachedDevice =
mBluetoothManager.getCachedDeviceManager().findDevice(device);
if (cachedDevice != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
new file mode 100644
index 0000000..3f19830
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
@@ -0,0 +1,160 @@
+/*
+ * 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.settingslib.notification.modes;
+
+import static com.google.common.util.concurrent.Futures.immediateFuture;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.Nullable;
+import android.app.AutomaticZenRule;
+import android.content.Context;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.service.notification.SystemZenRules;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LruCache;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.content.res.AppCompatResources;
+
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class ZenIconLoader {
+
+ private static final String TAG = "ZenIconLoader";
+
+ private static final Drawable MISSING = new ColorDrawable();
+
+ @Nullable // Until first usage
+ private static ZenIconLoader sInstance;
+
+ private final LruCache<String, Drawable> mCache;
+ private final ListeningExecutorService mBackgroundExecutor;
+
+ public static ZenIconLoader getInstance() {
+ if (sInstance == null) {
+ sInstance = new ZenIconLoader();
+ }
+ return sInstance;
+ }
+
+ private ZenIconLoader() {
+ this(Executors.newFixedThreadPool(4));
+ }
+
+ @VisibleForTesting
+ ZenIconLoader(ExecutorService backgroundExecutor) {
+ mCache = new LruCache<>(50);
+ mBackgroundExecutor =
+ MoreExecutors.listeningDecorator(backgroundExecutor);
+ }
+
+ @NonNull
+ ListenableFuture<Drawable> getIcon(Context context, @NonNull AutomaticZenRule rule) {
+ if (rule.getIconResId() == 0) {
+ return Futures.immediateFuture(getFallbackIcon(context, rule.getType()));
+ }
+
+ return FluentFuture.from(loadIcon(context, rule.getPackageName(), rule.getIconResId()))
+ .transform(icon ->
+ icon != null ? icon : getFallbackIcon(context, rule.getType()),
+ MoreExecutors.directExecutor());
+ }
+
+ @NonNull
+ private ListenableFuture</* @Nullable */ Drawable> loadIcon(Context context, String pkg,
+ int iconResId) {
+ String cacheKey = pkg + ":" + iconResId;
+ synchronized (mCache) {
+ Drawable cachedValue = mCache.get(cacheKey);
+ if (cachedValue != null) {
+ return immediateFuture(cachedValue != MISSING ? cachedValue : null);
+ }
+ }
+
+ return FluentFuture.from(mBackgroundExecutor.submit(() -> {
+ if (TextUtils.isEmpty(pkg) || SystemZenRules.PACKAGE_ANDROID.equals(pkg)) {
+ return context.getDrawable(iconResId);
+ } else {
+ Context appContext = context.createPackageContext(pkg, 0);
+ Drawable appDrawable = AppCompatResources.getDrawable(appContext, iconResId);
+ return getMonochromeIconIfPresent(appDrawable);
+ }
+ })).catching(Exception.class, ex -> {
+ // If we cannot resolve the icon, then store MISSING in the cache below, so
+ // we don't try again.
+ Log.e(TAG, "Error while loading icon " + cacheKey, ex);
+ return null;
+ }, MoreExecutors.directExecutor()).transform(drawable -> {
+ synchronized (mCache) {
+ mCache.put(cacheKey, drawable != null ? drawable : MISSING);
+ }
+ return drawable;
+ }, MoreExecutors.directExecutor());
+ }
+
+ private static Drawable getFallbackIcon(Context context, int ruleType) {
+ int iconResIdFromType = switch (ruleType) {
+ case AutomaticZenRule.TYPE_UNKNOWN ->
+ com.android.internal.R.drawable.ic_zen_mode_type_unknown;
+ case AutomaticZenRule.TYPE_OTHER ->
+ com.android.internal.R.drawable.ic_zen_mode_type_other;
+ case AutomaticZenRule.TYPE_SCHEDULE_TIME ->
+ com.android.internal.R.drawable.ic_zen_mode_type_schedule_time;
+ case AutomaticZenRule.TYPE_SCHEDULE_CALENDAR ->
+ com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar;
+ case AutomaticZenRule.TYPE_BEDTIME ->
+ com.android.internal.R.drawable.ic_zen_mode_type_bedtime;
+ case AutomaticZenRule.TYPE_DRIVING ->
+ com.android.internal.R.drawable.ic_zen_mode_type_driving;
+ case AutomaticZenRule.TYPE_IMMERSIVE ->
+ com.android.internal.R.drawable.ic_zen_mode_type_immersive;
+ case AutomaticZenRule.TYPE_THEATER ->
+ com.android.internal.R.drawable.ic_zen_mode_type_theater;
+ case AutomaticZenRule.TYPE_MANAGED ->
+ com.android.internal.R.drawable.ic_zen_mode_type_managed;
+ default -> com.android.internal.R.drawable.ic_zen_mode_type_unknown;
+ };
+ return requireNonNull(context.getDrawable(iconResIdFromType));
+ }
+
+ private static Drawable getMonochromeIconIfPresent(Drawable icon) {
+ // For created rules, the app should've provided a monochrome Drawable. However, implicit
+ // rules have the app's icon, which is not -- but might have a monochrome layer. Thus
+ // we choose it, if present.
+ if (icon instanceof AdaptiveIconDrawable adaptiveIcon) {
+ if (adaptiveIcon.getMonochrome() != null) {
+ // Wrap with negative inset => scale icon (inspired from BaseIconFactory)
+ return new InsetDrawable(adaptiveIcon.getMonochrome(),
+ -2.0f * AdaptiveIconDrawable.getExtraInsetFraction());
+ }
+ }
+ return icon;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
new file mode 100644
index 0000000..33d39f0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -0,0 +1,307 @@
+/*
+ * 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.settingslib.notification.modes;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
+import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
+import static android.service.notification.ZenModeConfig.tryParseEventConditionId;
+import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.SuppressLint;
+import android.app.AutomaticZenRule;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.service.notification.SystemZenRules;
+import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.R;
+
+import com.google.common.base.Strings;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Objects;
+
+/**
+ * Represents either an {@link AutomaticZenRule} or the manual DND rule in a unified way.
+ *
+ * <p>It also adapts other rule features that we don't want to expose in the UI, such as
+ * interruption filters other than {@code PRIORITY}, rules without specific icons, etc.
+ */
+public class ZenMode {
+
+ private static final String TAG = "ZenMode";
+
+ static final String MANUAL_DND_MODE_ID = "manual_dnd";
+
+ // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
+ private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
+ new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowPriorityChannels(false)
+ .build();
+
+ // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
+ private static final ZenPolicy POLICY_INTERRUPTION_FILTER_NONE =
+ new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .hideAllVisualEffects()
+ .allowPriorityChannels(false)
+ .build();
+
+ public enum Status {
+ ENABLED,
+ ENABLED_AND_ACTIVE,
+ DISABLED_BY_USER,
+ DISABLED_BY_OTHER
+ }
+
+ private final String mId;
+ private final AutomaticZenRule mRule;
+ private final Status mStatus;
+ private final boolean mIsManualDnd;
+
+ /**
+ * Initializes a {@link ZenMode}, mainly based on the information from the
+ * {@link AutomaticZenRule}.
+ *
+ * <p>Some pieces which are not part of the public API (such as whether the mode is currently
+ * active, or the reason it was disabled) are read from the {@link ZenModeConfig.ZenRule} --
+ * see {@link #computeStatus}.
+ */
+ public ZenMode(String id, @NonNull AutomaticZenRule rule,
+ @NonNull ZenModeConfig.ZenRule zenRuleExtraData) {
+ this(id, rule, computeStatus(zenRuleExtraData), false);
+ }
+
+ private static Status computeStatus(@NonNull ZenModeConfig.ZenRule zenRuleExtraData) {
+ if (zenRuleExtraData.enabled) {
+ if (zenRuleExtraData.isAutomaticActive()) {
+ return Status.ENABLED_AND_ACTIVE;
+ } else {
+ return Status.ENABLED;
+ }
+ } else {
+ if (zenRuleExtraData.disabledOrigin == ZenModeConfig.UPDATE_ORIGIN_USER) {
+ return Status.DISABLED_BY_USER;
+ } else {
+ return Status.DISABLED_BY_OTHER; // by APP, SYSTEM, UNKNOWN.
+ }
+ }
+ }
+
+ public static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
+ return new ZenMode(MANUAL_DND_MODE_ID, manualRule,
+ isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED, true);
+ }
+
+ private ZenMode(String id, @NonNull AutomaticZenRule rule, Status status, boolean isManualDnd) {
+ mId = id;
+ mRule = rule;
+ mStatus = status;
+ mIsManualDnd = isManualDnd;
+ }
+
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ @NonNull
+ public AutomaticZenRule getRule() {
+ return mRule;
+ }
+
+ @NonNull
+ public String getName() {
+ return Strings.nullToEmpty(mRule.getName());
+ }
+
+ @NonNull
+ public Status getStatus() {
+ return mStatus;
+ }
+
+ @AutomaticZenRule.Type
+ public int getType() {
+ return mRule.getType();
+ }
+
+ @Nullable
+ public String getTriggerDescription() {
+ return mRule.getTriggerDescription();
+ }
+
+ @NonNull
+ public ListenableFuture<Drawable> getIcon(@NonNull Context context,
+ @NonNull ZenIconLoader iconLoader) {
+ if (mIsManualDnd) {
+ 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()) {
+ case INTERRUPTION_FILTER_PRIORITY:
+ case NotificationManager.INTERRUPTION_FILTER_ALL:
+ return requireNonNull(mRule.getZenPolicy());
+
+ case NotificationManager.INTERRUPTION_FILTER_ALARMS:
+ return POLICY_INTERRUPTION_FILTER_ALARMS;
+
+ case NotificationManager.INTERRUPTION_FILTER_NONE:
+ return POLICY_INTERRUPTION_FILTER_NONE;
+
+ case NotificationManager.INTERRUPTION_FILTER_UNKNOWN:
+ default:
+ Log.wtf(TAG, "Rule " + mId + " with unexpected interruptionFilter "
+ + mRule.getInterruptionFilter());
+ return requireNonNull(mRule.getZenPolicy());
+ }
+ }
+
+ /**
+ * Updates the {@link ZenPolicy} of the associated {@link AutomaticZenRule} based on the
+ * supplied policy. In some cases this involves conversions, so that the following call
+ * to {@link #getPolicy} might return a different policy from the one supplied here.
+ */
+ @SuppressLint("WrongConstant")
+ public void setPolicy(@NonNull ZenPolicy policy) {
+ ZenPolicy currentPolicy = getPolicy();
+ if (currentPolicy.equals(policy)) {
+ return;
+ }
+
+ if (mRule.getInterruptionFilter() == INTERRUPTION_FILTER_ALL) {
+ Log.wtf(TAG, "Able to change policy without filtering being enabled");
+ }
+
+ // If policy is customized from any of the "special" ones, make the rule PRIORITY.
+ if (mRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
+ mRule.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
+ }
+ mRule.setZenPolicy(policy);
+ }
+
+ @NonNull
+ public ZenDeviceEffects getDeviceEffects() {
+ return mRule.getDeviceEffects() != null
+ ? mRule.getDeviceEffects()
+ : new ZenDeviceEffects.Builder().build();
+ }
+
+ public void setCustomModeConditionId(Context context, Uri conditionId) {
+ checkState(SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName()),
+ "Trying to change condition of non-system-owned rule %s (to %s)",
+ mRule, conditionId);
+
+ Uri oldCondition = mRule.getConditionId();
+ mRule.setConditionId(conditionId);
+
+ ZenModeConfig.ScheduleInfo scheduleInfo = tryParseScheduleConditionId(conditionId);
+ if (scheduleInfo != null) {
+ mRule.setType(AutomaticZenRule.TYPE_SCHEDULE_TIME);
+ mRule.setOwner(ZenModeConfig.getScheduleConditionProvider());
+ mRule.setTriggerDescription(
+ getTriggerDescriptionForScheduleTime(context, scheduleInfo));
+ return;
+ }
+
+ ZenModeConfig.EventInfo eventInfo = tryParseEventConditionId(conditionId);
+ if (eventInfo != null) {
+ mRule.setType(AutomaticZenRule.TYPE_SCHEDULE_CALENDAR);
+ mRule.setOwner(ZenModeConfig.getEventConditionProvider());
+ mRule.setTriggerDescription(getTriggerDescriptionForScheduleEvent(context, eventInfo));
+ return;
+ }
+
+ if (ZenModeConfig.isValidCustomManualConditionId(conditionId)) {
+ mRule.setType(AutomaticZenRule.TYPE_OTHER);
+ mRule.setOwner(ZenModeConfig.getCustomManualConditionProvider());
+ mRule.setTriggerDescription("");
+ return;
+ }
+
+ Log.wtf(TAG, String.format(
+ "Changed condition of rule %s (%s -> %s) but cannot recognize which kind of "
+ + "condition it was!",
+ mRule, oldCondition, conditionId));
+ }
+
+ public boolean canEditName() {
+ return !isManualDnd();
+ }
+
+ public boolean canEditIcon() {
+ return !isManualDnd();
+ }
+
+ public boolean canBeDeleted() {
+ return !isManualDnd();
+ }
+
+ public boolean isManualDnd() {
+ return mIsManualDnd;
+ }
+
+ public boolean isActive() {
+ return mStatus == Status.ENABLED_AND_ACTIVE;
+ }
+
+ public boolean isSystemOwned() {
+ return SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName());
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ return obj instanceof ZenMode other
+ && mId.equals(other.mId)
+ && mRule.equals(other.mRule)
+ && mStatus.equals(other.mStatus)
+ && mIsManualDnd == other.mIsManualDnd;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mRule, mStatus, mIsManualDnd);
+ }
+
+ @Override
+ public String toString() {
+ return mId + " (" + mStatus + ") -> " + mRule;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
new file mode 100644
index 0000000..5529da0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
@@ -0,0 +1,202 @@
+/*
+ * 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.settingslib.notification.modes;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AutomaticZenRule;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.service.notification.ZenModeConfig;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class used for Settings-NMS interactions related to Mode management.
+ *
+ * <p>This class converts {@link AutomaticZenRule} instances, as well as the manual zen mode,
+ * into the unified {@link ZenMode} format.
+ */
+public class ZenModesBackend {
+
+ private static final String TAG = "ZenModeBackend";
+
+ @Nullable // Until first usage
+ private static ZenModesBackend sInstance;
+
+ private final NotificationManager mNotificationManager;
+
+ private final Context mContext;
+
+ public static ZenModesBackend getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new ZenModesBackend(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+
+ ZenModesBackend(Context context) {
+ mContext = context;
+ mNotificationManager = context.getSystemService(NotificationManager.class);
+ }
+
+ public List<ZenMode> getModes() {
+ Map<String, AutomaticZenRule> zenRules = mNotificationManager.getAutomaticZenRules();
+ ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig();
+
+ ArrayList<ZenMode> modes = new ArrayList<>();
+ modes.add(getManualDndMode(currentConfig));
+
+ for (Map.Entry<String, AutomaticZenRule> zenRuleEntry : zenRules.entrySet()) {
+ String ruleId = zenRuleEntry.getKey();
+ ZenModeConfig.ZenRule extraData = currentConfig.automaticRules.get(ruleId);
+ if (extraData != null) {
+ modes.add(new ZenMode(ruleId, zenRuleEntry.getValue(), extraData));
+ } else {
+ Log.w(TAG, "Found AZR " + zenRuleEntry.getValue()
+ + " but no corresponding entry in ZenModeConfig (" + currentConfig
+ + "). Skipping");
+ }
+ }
+
+ // Manual DND first, then alphabetically.
+ modes.sort(Comparator.comparing(ZenMode::isManualDnd).reversed()
+ .thenComparing(ZenMode::getName));
+
+ return modes;
+ }
+
+ @Nullable
+ public ZenMode getMode(String id) {
+ ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig();
+ if (ZenMode.MANUAL_DND_MODE_ID.equals(id)) {
+ return getManualDndMode(currentConfig);
+ } else {
+ AutomaticZenRule rule = mNotificationManager.getAutomaticZenRule(id);
+ ZenModeConfig.ZenRule extraData = currentConfig.automaticRules.get(id);
+ if (rule == null || extraData == null) {
+ return null;
+ }
+ return new ZenMode(id, rule, extraData);
+ }
+ }
+
+ private ZenMode getManualDndMode(ZenModeConfig config) {
+ ZenModeConfig.ZenRule manualRule = config.manualRule;
+ // TODO: b/333682392 - Replace with final strings for name & trigger description
+ AutomaticZenRule manualDndRule = new AutomaticZenRule.Builder(
+ mContext.getString(R.string.zen_mode_settings_title), manualRule.conditionId)
+ .setType(manualRule.type)
+ .setZenPolicy(manualRule.zenPolicy)
+ .setDeviceEffects(manualRule.zenDeviceEffects)
+ .setManualInvocationAllowed(manualRule.allowManualInvocation)
+ .setConfigurationActivity(null) // No further settings
+ .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+ .build();
+
+ return ZenMode.manualDndMode(manualDndRule, config != null && config.isManualActive());
+ }
+
+ public void updateMode(ZenMode mode) {
+ if (mode.isManualDnd()) {
+ try {
+ NotificationManager.Policy dndPolicy =
+ new ZenModeConfig().toNotificationPolicy(mode.getPolicy());
+ mNotificationManager.setNotificationPolicy(dndPolicy, /* fromUser= */ true);
+
+ mNotificationManager.setManualZenRuleDeviceEffects(
+ mode.getRule().getDeviceEffects());
+ } catch (Exception e) {
+ Log.w(TAG, "Error updating manual mode", e);
+ }
+ } else {
+ mNotificationManager.updateAutomaticZenRule(mode.getId(), mode.getRule(),
+ /* fromUser= */ true);
+ }
+ }
+
+ public void activateMode(ZenMode mode, @Nullable Duration forDuration) {
+ if (mode.isManualDnd()) {
+ Uri durationConditionId = null;
+ if (forDuration != null) {
+ durationConditionId = ZenModeConfig.toTimeCondition(mContext,
+ (int) forDuration.toMinutes(), ActivityManager.getCurrentUser(), true).id;
+ }
+ mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ durationConditionId, TAG, /* fromUser= */ true);
+
+ } else {
+ if (forDuration != null) {
+ throw new IllegalArgumentException(
+ "Only the manual DND mode can be activated for a specific duration");
+ }
+ mNotificationManager.setAutomaticZenRuleState(mode.getId(),
+ new Condition(mode.getRule().getConditionId(), "", Condition.STATE_TRUE,
+ Condition.SOURCE_USER_ACTION));
+ }
+ }
+
+ public void deactivateMode(ZenMode mode) {
+ if (mode.isManualDnd()) {
+ // When calling with fromUser=true this will not snooze other modes.
+ mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG,
+ /* fromUser= */ true);
+ } else {
+ // TODO: b/333527800 - This should (potentially) snooze the rule if it was active.
+ mNotificationManager.setAutomaticZenRuleState(mode.getId(),
+ new Condition(mode.getRule().getConditionId(), "", Condition.STATE_FALSE,
+ Condition.SOURCE_USER_ACTION));
+ }
+ }
+
+ public void removeMode(ZenMode mode) {
+ if (!mode.canBeDeleted()) {
+ throw new IllegalArgumentException("Mode " + mode + " cannot be deleted!");
+ }
+ mNotificationManager.removeAutomaticZenRule(mode.getId(), /* fromUser= */ true);
+ }
+
+ /**
+ * Creates a new custom mode with the provided {@code name}. The mode will be "manual" (i.e.
+ * not have a schedule), this can be later updated by the user in the mode settings page.
+ *
+ * @return the created mode. Only {@code null} if creation failed due to an internal error
+ */
+ @Nullable
+ public ZenMode addCustomMode(String name) {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder(name,
+ ZenModeConfig.toCustomManualConditionId())
+ .setPackage(ZenModeConfig.getCustomManualConditionProvider().getPackageName())
+ .setType(AutomaticZenRule.TYPE_OTHER)
+ .setOwner(ZenModeConfig.getCustomManualConditionProvider())
+ .setManualInvocationAllowed(true)
+ .build();
+
+ String ruleId = mNotificationManager.addAutomaticZenRule(rule);
+ return getMode(ruleId);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/model/ZenMode.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/model/ZenMode.kt
deleted file mode 100644
index a696f8c..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/model/ZenMode.kt
+++ /dev/null
@@ -1,39 +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.settingslib.statusbar.notification.data.model
-
-import android.provider.Settings.Global
-
-/** Validating wrapper for [android.app.NotificationManager.getZenMode] values. */
-@JvmInline
-value class ZenMode(val zenMode: Int) {
-
- init {
- require(zenMode in supportedModes) { "Unsupported zenMode=$zenMode" }
- }
-
- private companion object {
-
- val supportedModes =
- listOf(
- Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- Global.ZEN_MODE_NO_INTERRUPTIONS,
- Global.ZEN_MODE_ALARMS,
- Global.ZEN_MODE_OFF,
- )
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeZenModeRepository.kt
similarity index 80%
rename from packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
rename to packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeZenModeRepository.kt
index a939ed1..775e2fc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeZenModeRepository.kt
@@ -18,19 +18,18 @@
import android.app.NotificationManager
import android.provider.Settings
-import com.android.settingslib.statusbar.notification.data.model.ZenMode
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeNotificationsSoundPolicyRepository : NotificationsSoundPolicyRepository {
+class FakeZenModeRepository : ZenModeRepository {
private val mutableNotificationPolicy = MutableStateFlow<NotificationManager.Policy?>(null)
- override val notificationPolicy: StateFlow<NotificationManager.Policy?>
+ override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?>
get() = mutableNotificationPolicy.asStateFlow()
- private val mutableZenMode = MutableStateFlow<ZenMode?>(ZenMode(Settings.Global.ZEN_MODE_OFF))
- override val zenMode: StateFlow<ZenMode?>
+ private val mutableZenMode = MutableStateFlow(Settings.Global.ZEN_MODE_OFF)
+ override val globalZenMode: StateFlow<Int>
get() = mutableZenMode.asStateFlow()
init {
@@ -41,12 +40,12 @@
mutableNotificationPolicy.value = policy
}
- fun updateZenMode(zenMode: ZenMode?) {
+ fun updateZenMode(zenMode: Int) {
mutableZenMode.value = zenMode
}
}
-fun FakeNotificationsSoundPolicyRepository.updateNotificationPolicy(
+fun FakeZenModeRepository.updateNotificationPolicy(
priorityCategories: Int = 0,
priorityCallSenders: Int = NotificationManager.Policy.PRIORITY_SENDERS_ANY,
priorityMessageSenders: Int = NotificationManager.Policy.CONVERSATION_SENDERS_NONE,
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/NotificationsSoundPolicyRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/NotificationsSoundPolicyRepository.kt
deleted file mode 100644
index 0fb8c3f..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/NotificationsSoundPolicyRepository.kt
+++ /dev/null
@@ -1,95 +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.settingslib.statusbar.notification.data.repository
-
-import android.app.NotificationManager
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import com.android.settingslib.statusbar.notification.data.model.ZenMode
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
-
-/** Provides state of volume policy and restrictions imposed by notifications. */
-interface NotificationsSoundPolicyRepository {
-
- /** @see NotificationManager.getNotificationPolicy */
- val notificationPolicy: StateFlow<NotificationManager.Policy?>
-
- /** @see NotificationManager.getZenMode */
- val zenMode: StateFlow<ZenMode?>
-}
-
-class NotificationsSoundPolicyRepositoryImpl(
- private val context: Context,
- private val notificationManager: NotificationManager,
- scope: CoroutineScope,
- backgroundCoroutineContext: CoroutineContext,
-) : NotificationsSoundPolicyRepository {
-
- private val notificationBroadcasts =
- callbackFlow {
- val receiver =
- object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- intent?.action?.let { action -> launch { send(action) } }
- }
- }
-
- context.registerReceiver(
- receiver,
- IntentFilter().apply {
- addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)
- addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
- }
- )
-
- awaitClose { context.unregisterReceiver(receiver) }
- }
- .shareIn(
- started = SharingStarted.WhileSubscribed(),
- scope = scope,
- )
-
- override val notificationPolicy: StateFlow<NotificationManager.Policy?> =
- notificationBroadcasts
- .filter { NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED == it }
- .map { notificationManager.consolidatedNotificationPolicy }
- .onStart { emit(notificationManager.consolidatedNotificationPolicy) }
- .flowOn(backgroundCoroutineContext)
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
-
- override val zenMode: StateFlow<ZenMode?> =
- notificationBroadcasts
- .filter { NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED == it }
- .map { ZenMode(notificationManager.zenMode) }
- .onStart { emit(ZenMode(notificationManager.zenMode)) }
- .flowOn(backgroundCoroutineContext)
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt
new file mode 100644
index 0000000..4d25237
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.settingslib.statusbar.notification.data.repository
+
+import android.app.NotificationManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import com.android.settingslib.flags.Flags
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/** Provides state of volume policy and restrictions imposed by notifications. */
+interface ZenModeRepository {
+ /** @see NotificationManager.getConsolidatedNotificationPolicy */
+ val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?>
+
+ /** @see NotificationManager.getZenMode */
+ val globalZenMode: StateFlow<Int?>
+}
+
+class ZenModeRepositoryImpl(
+ private val context: Context,
+ private val notificationManager: NotificationManager,
+ val scope: CoroutineScope,
+ val backgroundCoroutineContext: CoroutineContext,
+) : ZenModeRepository {
+
+ private val notificationBroadcasts =
+ callbackFlow {
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ intent?.action?.let { action -> launch { send(action) } }
+ }
+ }
+
+ context.registerReceiver(
+ receiver,
+ IntentFilter().apply {
+ addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)
+ addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
+ if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi())
+ addAction(
+ NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED)
+ })
+
+ awaitClose { context.unregisterReceiver(receiver) }
+ }
+ .apply {
+ if (Flags.volumePanelBroadcastFix()) {
+ flowOn(backgroundCoroutineContext)
+ stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ } else {
+ shareIn(
+ started = SharingStarted.WhileSubscribed(),
+ scope = scope,
+ )
+ }
+ }
+
+ override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?> =
+ if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi())
+ flowFromBroadcast(NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED) {
+ notificationManager.consolidatedNotificationPolicy
+ }
+ else
+ flowFromBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED) {
+ notificationManager.consolidatedNotificationPolicy
+ }
+
+ override val globalZenMode: StateFlow<Int?> =
+ flowFromBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED) {
+ notificationManager.zenMode
+ }
+
+ private fun <T> flowFromBroadcast(intentAction: String, mapper: () -> T) =
+ notificationBroadcasts
+ .filter { intentAction == it }
+ .map { mapper() }
+ .onStart { emit(mapper()) }
+ .flowOn(backgroundCoroutineContext)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
index 7719c4b..953c90d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
@@ -20,8 +20,7 @@
import android.media.AudioManager
import android.provider.Settings
import android.service.notification.ZenModeConfig
-import com.android.settingslib.statusbar.notification.data.model.ZenMode
-import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository
+import com.android.settingslib.statusbar.notification.data.repository.ZenModeRepository
import com.android.settingslib.volume.shared.model.AudioStream
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -30,17 +29,15 @@
import kotlinx.coroutines.flow.map
/** Determines notification sounds state and limitations. */
-class NotificationsSoundPolicyInteractor(
- private val repository: NotificationsSoundPolicyRepository
-) {
+class NotificationsSoundPolicyInteractor(private val repository: ZenModeRepository) {
/** @see NotificationManager.getNotificationPolicy */
- val notificationPolicy: StateFlow<NotificationManager.Policy?>
- get() = repository.notificationPolicy
+ private val notificationPolicy: StateFlow<NotificationManager.Policy?>
+ get() = repository.consolidatedNotificationPolicy
/** @see NotificationManager.getZenMode */
- val zenMode: StateFlow<ZenMode?>
- get() = repository.zenMode
+ val zenMode: StateFlow<Int?>
+ get() = repository.globalZenMode
/** Checks if [notificationPolicy] allows alarms. */
val areAlarmsAllowed: Flow<Boolean?> = notificationPolicy.map { it?.allowAlarms() }
@@ -67,7 +64,7 @@
isRingerAllowed.filterNotNull(),
isSystemAllowed.filterNotNull(),
) { zenMode, areAlarmsAllowed, isMediaAllowed, isRingerAllowed, isSystemAllowed ->
- when (zenMode.zenMode) {
+ when (zenMode) {
// Everything is muted
Settings.Global.ZEN_MODE_NO_INTERRUPTIONS -> return@combine true
Settings.Global.ZEN_MODE_ALARMS ->
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 20b949f4..837c682 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -20,6 +20,7 @@
import android.database.ContentObserver
import android.media.AudioDeviceInfo
import android.media.AudioManager
+import android.media.AudioManager.AudioDeviceCategory
import android.media.AudioManager.OnCommunicationDeviceChangedListener
import android.provider.Settings
import androidx.concurrent.futures.DirectExecutor
@@ -45,6 +46,7 @@
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -85,6 +87,10 @@
suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean
suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode)
+
+ /** Gets audio device category. */
+ @AudioDeviceCategory
+ suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int
}
class AudioRepositoryImpl(
@@ -93,6 +99,7 @@
private val contentResolver: ContentResolver,
private val backgroundCoroutineContext: CoroutineContext,
private val coroutineScope: CoroutineScope,
+ private val logger: Logger,
) : AudioRepository {
private val streamSettingNames: Map<AudioStream, String> =
@@ -165,6 +172,7 @@
.conflate()
.map { getCurrentAudioStream(audioStream) }
.onStart { emit(getCurrentAudioStream(audioStream)) }
+ .onEach { logger.onVolumeUpdateReceived(audioStream, it) }
.flowOn(backgroundCoroutineContext)
}
@@ -188,6 +196,7 @@
override suspend fun setVolume(audioStream: AudioStream, volume: Int) {
withContext(backgroundCoroutineContext) {
+ logger.onSetVolumeRequested(audioStream, volume)
audioManager.setStreamVolume(audioStream.value, volume, 0)
}
}
@@ -211,6 +220,13 @@
withContext(backgroundCoroutineContext) { audioManager.ringerMode = mode.value }
}
+ @AudioDeviceCategory
+ override suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int {
+ return withContext(backgroundCoroutineContext) {
+ audioManager.getBluetoothAudioDeviceCategory(bluetoothAddress)
+ }
+ }
+
private fun getMinVolume(stream: AudioStream): Int =
try {
audioManager.getStreamMinVolume(stream.value)
@@ -235,4 +251,11 @@
awaitClose { contentResolver.unregisterContentObserver(observer) }
}
}
+
+ interface Logger {
+
+ fun onSetVolumeRequested(audioStream: AudioStream, volume: Int)
+
+ fun onVolumeUpdateReceived(audioStream: AudioStream, model: AudioStreamModel)
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
index 9c48299..c8e4d71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.volume.shared.model
import android.media.AudioManager
+import android.media.AudioSystem
/** Type-safe wrapper for [AudioManager] audio stream. */
@JvmInline
@@ -25,6 +26,8 @@
require(value in supportedStreamTypes) { "Unsupported stream=$value" }
}
+ override fun toString(): String = AudioSystem.streamToString(value)
+
companion object {
val supportedStreamTypes =
setOf(
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/statusbar/notification/data/repository/NotificationsSoundPolicyRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepositoryTest.kt
similarity index 66%
rename from packages/SettingsLib/tests/integ/src/com/android/settingslib/statusbar/notification/data/repository/NotificationsSoundPolicyRepositoryTest.kt
rename to packages/SettingsLib/tests/integ/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepositoryTest.kt
index dfc4c0a..688bebb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/statusbar/notification/data/repository/NotificationsSoundPolicyRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepositoryTest.kt
@@ -20,10 +20,12 @@
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.provider.Settings.Global
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.statusbar.notification.data.model.ZenMode
+import com.android.settingslib.flags.Flags
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -45,13 +47,15 @@
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
-class NotificationsSoundPolicyRepositoryTest {
+class ZenModeRepositoryTest {
@Mock private lateinit var context: Context
+
@Mock private lateinit var notificationManager: NotificationManager
+
@Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
- private lateinit var underTest: NotificationsSoundPolicyRepository
+ private lateinit var underTest: ZenModeRepository
private val testScope: TestScope = TestScope()
@@ -60,7 +64,7 @@
MockitoAnnotations.initMocks(this)
underTest =
- NotificationsSoundPolicyRepositoryImpl(
+ ZenModeRepositoryImpl(
context,
notificationManager,
testScope.backgroundScope,
@@ -68,15 +72,18 @@
)
}
+ @DisableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
@Test
- fun policyChanges_repositoryEmits() {
+ fun consolidatedPolicyChanges_repositoryEmits_flagsOff() {
testScope.runTest {
val values = mutableListOf<NotificationManager.Policy?>()
- `when`(notificationManager.notificationPolicy).thenReturn(testPolicy1)
- underTest.notificationPolicy.onEach { values.add(it) }.launchIn(backgroundScope)
+ `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1)
+ underTest.consolidatedNotificationPolicy
+ .onEach { values.add(it) }
+ .launchIn(backgroundScope)
runCurrent()
- `when`(notificationManager.notificationPolicy).thenReturn(testPolicy2)
+ `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy2)
triggerIntent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED)
runCurrent()
@@ -86,12 +93,33 @@
}
}
+ @EnableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX)
+ @Test
+ fun consolidatedPolicyChanges_repositoryEmits_flagsOn() {
+ testScope.runTest {
+ val values = mutableListOf<NotificationManager.Policy?>()
+ `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1)
+ underTest.consolidatedNotificationPolicy
+ .onEach { values.add(it) }
+ .launchIn(backgroundScope)
+ runCurrent()
+
+ `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy2)
+ triggerIntent(NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactlyElementsIn(listOf(null, testPolicy1, testPolicy2))
+ .inOrder()
+ }
+ }
+
@Test
fun zenModeChanges_repositoryEmits() {
testScope.runTest {
- val values = mutableListOf<ZenMode?>()
+ val values = mutableListOf<Int?>()
`when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_OFF)
- underTest.zenMode.onEach { values.add(it) }.launchIn(backgroundScope)
+ underTest.globalZenMode.onEach { values.add(it) }.launchIn(backgroundScope)
runCurrent()
`when`(notificationManager.zenMode).thenReturn(Global.ZEN_MODE_ALARMS)
@@ -100,8 +128,7 @@
assertThat(values)
.containsExactlyElementsIn(
- listOf(null, ZenMode(Global.ZEN_MODE_OFF), ZenMode(Global.ZEN_MODE_ALARMS))
- )
+ listOf(null, Global.ZEN_MODE_OFF, Global.ZEN_MODE_ALARMS))
.inOrder()
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 683759d..0e43acb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -64,6 +64,7 @@
@Mock private lateinit var communicationDevice: AudioDeviceInfo
@Mock private lateinit var contentResolver: ContentResolver
+ private val logger = FakeAudioRepositoryLogger()
private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val volumeByStream: MutableMap<Int, Int> = mutableMapOf()
private val isAffectedByRingerModeByStream: MutableMap<Int, Boolean> = mutableMapOf()
@@ -109,6 +110,7 @@
contentResolver,
testScope.testScheduler,
testScope.backgroundScope,
+ logger,
)
}
@@ -173,6 +175,15 @@
underTest.setVolume(audioStream, 50)
runCurrent()
+ assertThat(logger.logs)
+ .isEqualTo(
+ listOf(
+ "onVolumeUpdateReceived audioStream=STREAM_SYSTEM",
+ "onSetVolumeRequested audioStream=STREAM_SYSTEM",
+ "onVolumeUpdateReceived audioStream=STREAM_SYSTEM",
+ "onVolumeUpdateReceived audioStream=STREAM_SYSTEM",
+ )
+ )
assertThat(streamModel)
.isEqualTo(
AudioStreamModel(
@@ -247,6 +258,19 @@
}
}
+ @Test
+ fun getBluetoothAudioDeviceCategory() {
+ testScope.runTest {
+ `when`(audioManager.getBluetoothAudioDeviceCategory("12:34:56:78")).thenReturn(
+ AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES)
+
+ val category = underTest.getBluetoothAudioDeviceCategory("12:34:56:78")
+ runCurrent()
+
+ assertThat(category).isEqualTo(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES)
+ }
+ }
+
private fun triggerConnectedDeviceChange(communicationDevice: AudioDeviceInfo?) {
verify(audioManager)
.addOnCommunicationDeviceChangedListener(
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepositoryLogger.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepositoryLogger.kt
new file mode 100644
index 0000000..389bf53
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepositoryLogger.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.settingslib.volume.data.repository
+
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.AudioStreamModel
+
+class FakeAudioRepositoryLogger : AudioRepositoryImpl.Logger {
+
+ private val mutableLogs: MutableList<String> = mutableListOf()
+ val logs: List<String>
+ get() = mutableLogs
+
+ override fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
+ synchronized(mutableLogs) {
+ mutableLogs.add("onSetVolumeRequested audioStream=$audioStream")
+ }
+ }
+
+ override fun onVolumeUpdateReceived(audioStream: AudioStream, model: AudioStreamModel) {
+ synchronized(mutableLogs) {
+ mutableLogs.add("onVolumeUpdateReceived audioStream=$audioStream")
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenIconLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenIconLoaderTest.java
new file mode 100644
index 0000000..20461e3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenIconLoaderTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.settingslib.notification.modes;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AutomaticZenRule;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.service.notification.ZenPolicy;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenIconLoaderTest {
+
+ private Context mContext;
+ private ZenIconLoader mLoader;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mLoader = new ZenIconLoader(MoreExecutors.newDirectExecutorService());
+ }
+
+ @Test
+ public void getIcon_systemOwnedRuleWithIcon_loads() throws Exception {
+ AutomaticZenRule systemRule = newRuleBuilder()
+ .setPackage("android")
+ .setIconResId(android.R.drawable.ic_media_play)
+ .build();
+
+ ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, systemRule);
+ assertThat(loadFuture.isDone()).isTrue();
+ assertThat(loadFuture.get()).isNotNull();
+ }
+
+ @Test
+ public void getIcon_ruleWithoutSpecificIcon_loadsFallback() throws Exception {
+ AutomaticZenRule rule = newRuleBuilder()
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setPackage("com.blah")
+ .build();
+
+ ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
+ assertThat(loadFuture.isDone()).isTrue();
+ assertThat(loadFuture.get()).isNotNull();
+ }
+
+ @Test
+ public void getIcon_ruleWithAppIconWithLoadFailure_loadsFallback() throws Exception {
+ AutomaticZenRule rule = newRuleBuilder()
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setPackage("com.blah")
+ .setIconResId(-123456)
+ .build();
+
+ ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
+ assertThat(loadFuture.get()).isNotNull();
+ }
+
+ private static AutomaticZenRule.Builder newRuleBuilder() {
+ return new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().build());
+ }
+}
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
new file mode 100644
index 0000000..32cdb98
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.settingslib.notification.modes;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AutomaticZenRule;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenModeTest {
+
+ private static final ZenPolicy ZEN_POLICY = new ZenPolicy.Builder().allowAllSounds().build();
+
+ private static final AutomaticZenRule ZEN_RULE =
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setPackage("com.some.driving.thing")
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(ZEN_POLICY)
+ .build();
+
+ @Test
+ public void testBasicMethods() {
+ ZenMode zenMode = new ZenMode("id", ZEN_RULE, zenConfigRuleFor(ZEN_RULE, true));
+
+ assertThat(zenMode.getId()).isEqualTo("id");
+ assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE);
+ assertThat(zenMode.isManualDnd()).isFalse();
+ assertThat(zenMode.canBeDeleted()).isTrue();
+ assertThat(zenMode.isActive()).isTrue();
+
+ ZenMode manualMode = ZenMode.manualDndMode(ZEN_RULE, false);
+ assertThat(manualMode.getId()).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
+ assertThat(manualMode.isManualDnd()).isTrue();
+ assertThat(manualMode.canBeDeleted()).isFalse();
+ assertThat(manualMode.isActive()).isFalse();
+ }
+
+ @Test
+ public void constructor_enabledRule_statusEnabled() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(true).build();
+ ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, false);
+
+ ZenMode mode = new ZenMode("id", azr, configZenRule);
+ assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.ENABLED);
+ assertThat(mode.isActive()).isFalse();
+ }
+
+ @Test
+ public void constructor_activeRule_statusActive() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(true).build();
+ ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, true);
+
+ ZenMode mode = new ZenMode("id", azr, configZenRule);
+ assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.ENABLED_AND_ACTIVE);
+ assertThat(mode.isActive()).isTrue();
+ }
+
+ @Test
+ public void constructor_disabledRuleByUser_statusDisabledByUser() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(false).build();
+ ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, false);
+ configZenRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_USER;
+
+ ZenMode mode = new ZenMode("id", azr, configZenRule);
+ assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.DISABLED_BY_USER);
+ }
+
+ @Test
+ public void constructor_disabledRuleByOther_statusDisabledByOther() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(false).build();
+ ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, false);
+ configZenRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_APP;
+
+ ZenMode mode = new ZenMode("id", azr, configZenRule);
+ assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.DISABLED_BY_OTHER);
+ }
+
+ @Test
+ public void getPolicy_interruptionFilterPriority_returnsZenPolicy() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(ZEN_POLICY)
+ .build();
+ ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
+
+ assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
+ }
+
+ @Test
+ public void getPolicy_interruptionFilterAlarms_returnsPolicyAllowingAlarms() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+ .setZenPolicy(ZEN_POLICY) // should be ignored
+ .build();
+ ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
+
+ assertThat(zenMode.getPolicy()).isEqualTo(
+ new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowPriorityChannels(false)
+ .build());
+ }
+
+ @Test
+ public void getPolicy_interruptionFilterNone_returnsPolicyAllowingNothing() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+ .setZenPolicy(ZEN_POLICY) // should be ignored
+ .build();
+ ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
+
+ assertThat(zenMode.getPolicy()).isEqualTo(
+ new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .hideAllVisualEffects()
+ .allowPriorityChannels(false)
+ .build());
+ }
+
+ @Test
+ public void setPolicy_setsInterruptionFilterPriority() {
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+ .build();
+ ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
+
+ zenMode.setPolicy(ZEN_POLICY);
+
+ assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(
+ INTERRUPTION_FILTER_PRIORITY);
+ assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
+ assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY);
+ }
+
+ private static ZenModeConfig.ZenRule zenConfigRuleFor(AutomaticZenRule azr, boolean isActive) {
+ ZenModeConfig.ZenRule zenRule = new ZenModeConfig.ZenRule();
+ zenRule.pkg = azr.getPackageName();
+ zenRule.conditionId = azr.getConditionId();
+ zenRule.enabled = azr.isEnabled();
+ if (isActive) {
+ zenRule.condition = new Condition(azr.getConditionId(), "active", Condition.STATE_TRUE);
+ }
+ return zenRule;
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
new file mode 100644
index 0000000..00c7ae3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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.settingslib.notification.modes;
+
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.Condition.SOURCE_UNKNOWN;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.ZenAdapters.notificationPolicyToZenPolicy;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.app.NotificationManager;
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+
+import com.android.settingslib.R;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.time.Duration;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public class ZenModesBackendTest {
+
+ private static final String ZEN_RULE_ID = "rule";
+ private static final AutomaticZenRule ZEN_RULE =
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .build();
+
+ private static final AutomaticZenRule MANUAL_DND_RULE =
+ new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .build();
+
+ @Mock
+ private NotificationManager mNm;
+
+ private Context mContext;
+ private ZenModesBackend mBackend;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+ SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+
+ // Helper methods to add active/inactive rule state to a config. Returns a copy.
+ private static ZenModeConfig configWithManualRule(ZenModeConfig base, boolean active) {
+ ZenModeConfig out = base.copy();
+
+ if (active) {
+ out.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ out.manualRule.condition =
+ new Condition(out.manualRule.conditionId, "", STATE_TRUE, SOURCE_UNKNOWN);
+ } else {
+ out.manualRule.zenMode = ZEN_MODE_OFF;
+ out.manualRule.condition =
+ new Condition(out.manualRule.conditionId, "", STATE_FALSE, SOURCE_UNKNOWN);
+ }
+ return out;
+ }
+
+ private static ZenModeConfig configWithRule(ZenModeConfig base, String ruleId,
+ AutomaticZenRule rule, boolean active) {
+ ZenModeConfig out = base.copy();
+ out.automaticRules.put(ruleId, zenConfigRuleForRule(ruleId, rule, active));
+ return out;
+ }
+
+ private static ZenModeConfig.ZenRule zenConfigRuleForRule(String id, AutomaticZenRule azr,
+ boolean active) {
+ // Note that there are many other fields of zenRule, but here we only set the ones
+ // relevant to determining whether or not it is active.
+ ZenModeConfig.ZenRule zenRule = new ZenModeConfig.ZenRule();
+ zenRule.id = id;
+ zenRule.pkg = "package";
+ zenRule.enabled = azr.isEnabled();
+ zenRule.snoozing = false;
+ zenRule.conditionId = azr.getConditionId();
+ zenRule.condition = new Condition(azr.getConditionId(), "",
+ active ? Condition.STATE_TRUE : Condition.STATE_FALSE,
+ Condition.SOURCE_USER_ACTION);
+ return zenRule;
+ }
+
+ private static ZenMode newZenMode(String id, AutomaticZenRule azr, boolean active) {
+ return new ZenMode(id, azr, zenConfigRuleForRule(id, azr, active));
+ }
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+
+ mContext = RuntimeEnvironment.application;
+ mBackend = new ZenModesBackend(mContext);
+
+ // Default catch-all case with no data. This isn't realistic, but tests below that rely
+ // on the config to get data on rules active will create those individually.
+ when(mNm.getZenModeConfig()).thenReturn(new ZenModeConfig());
+ }
+
+ @Test
+ public void getModes_containsManualDndAndZenRules() {
+ AutomaticZenRule rule2 = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
+ .build();
+ Policy dndPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
+ Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
+ when(mNm.getAutomaticZenRules()).thenReturn(
+ ImmutableMap.of("rule1", ZEN_RULE, "rule2", rule2));
+
+ ZenModeConfig config = new ZenModeConfig();
+ config.applyNotificationPolicy(dndPolicy);
+ config = configWithRule(config, "rule1", ZEN_RULE, false);
+ config = configWithRule(config, "rule2", rule2, false);
+ assertThat(config.manualRule.zenPolicy.getPriorityCategoryAlarms()).isEqualTo(STATE_ALLOW);
+ when(mNm.getZenModeConfig()).thenReturn(config);
+
+ List<ZenMode> modes = mBackend.getModes();
+
+ // all modes exist, but none of them are currently active
+ assertThat(modes).containsExactly(
+ ZenMode.manualDndMode(
+ new AutomaticZenRule.Builder(
+ mContext.getString(R.string.zen_mode_settings_title),
+ Uri.EMPTY)
+ .setType(AutomaticZenRule.TYPE_OTHER)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
+ .setManualInvocationAllowed(true)
+ .build(),
+ false),
+ newZenMode("rule2", rule2, false),
+ newZenMode("rule1", ZEN_RULE, false))
+ .inOrder();
+ }
+
+ @Test
+ public void getMode_manualDnd_returnsMode() {
+ Policy dndPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
+ Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
+ ZenModeConfig config = new ZenModeConfig();
+ config.applyNotificationPolicy(dndPolicy);
+ when(mNm.getZenModeConfig()).thenReturn(config);
+
+ ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
+
+ assertThat(mode).isEqualTo(
+ ZenMode.manualDndMode(
+ new AutomaticZenRule.Builder(
+ mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
+ .setType(AutomaticZenRule.TYPE_OTHER)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
+ .setManualInvocationAllowed(true)
+ .build(), false));
+ }
+
+ @Test
+ public void getMode_zenRule_returnsMode() {
+ when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE);
+ when(mNm.getZenModeConfig()).thenReturn(
+ configWithRule(new ZenModeConfig(), ZEN_RULE_ID, ZEN_RULE, false));
+
+ ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
+
+ assertThat(mode).isEqualTo(newZenMode(ZEN_RULE_ID, ZEN_RULE, false));
+ }
+
+ @Test
+ public void getMode_missingRule_returnsNull() {
+ when(mNm.getAutomaticZenRule(any())).thenReturn(null);
+
+ ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
+
+ assertThat(mode).isNull();
+ verify(mNm).getAutomaticZenRule(eq(ZEN_RULE_ID));
+ }
+
+ @Test
+ public void getMode_manualDnd_returnsCorrectActiveState() {
+ // Set up a base config with an active rule to make sure we're looking at the correct info
+ ZenModeConfig configWithActiveRule = configWithRule(new ZenModeConfig(), ZEN_RULE_ID,
+ ZEN_RULE, true);
+
+ // Equivalent to disallowAllSounds()
+ Policy dndPolicy = new Policy(0, 0, 0);
+ configWithActiveRule.applyNotificationPolicy(dndPolicy);
+ when(mNm.getZenModeConfig()).thenReturn(configWithActiveRule);
+
+ ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
+
+ // By default, manual rule is inactive
+ assertThat(mode).isNotNull();
+ assertThat(mode.isActive()).isFalse();
+
+ // Now the returned config will represent the manual rule being active
+ when(mNm.getZenModeConfig()).thenReturn(configWithManualRule(configWithActiveRule, true));
+ ZenMode activeMode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
+ assertThat(activeMode).isNotNull();
+ assertThat(activeMode.isActive()).isTrue();
+ }
+
+ @Test
+ public void getMode_zenRule_returnsCorrectActiveState() {
+ // Set up a base config that has an active manual rule and "rule2", to make sure we're
+ // looking at the correct rule's info.
+ ZenModeConfig configWithActiveRules = configWithRule(
+ configWithManualRule(new ZenModeConfig(), true), // active manual rule
+ "rule2", ZEN_RULE, true); // active rule 2
+
+ when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE);
+ when(mNm.getZenModeConfig()).thenReturn(
+ configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, false));
+
+ // Round 1: the current config should indicate that the rule is not active
+ ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
+ assertThat(mode).isNotNull();
+ assertThat(mode.isActive()).isFalse();
+
+ when(mNm.getZenModeConfig()).thenReturn(
+ configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, true));
+ ZenMode activeMode = mBackend.getMode(ZEN_RULE_ID);
+ assertThat(activeMode).isNotNull();
+ assertThat(activeMode.isActive()).isTrue();
+ }
+
+ @Test
+ public void updateMode_manualDnd_setsDeviceEffects() throws Exception {
+ ZenMode manualDnd = ZenMode.manualDndMode(
+ new AutomaticZenRule.Builder("DND", Uri.EMPTY)
+ .setZenPolicy(new ZenPolicy())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build())
+ .build(), false);
+
+ mBackend.updateMode(manualDnd);
+
+ verify(mNm).setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build());
+ }
+
+ @Test
+ public void updateMode_manualDnd_setsNotificationPolicy() {
+ ZenMode manualDnd = ZenMode.manualDndMode(
+ new AutomaticZenRule.Builder("DND", Uri.EMPTY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .build(), false);
+
+ mBackend.updateMode(manualDnd);
+
+ verify(mNm).setNotificationPolicy(eq(new ZenModeConfig().toNotificationPolicy(
+ new ZenPolicy.Builder().allowAllSounds().build())), eq(true));
+ }
+
+ @Test
+ public void updateMode_zenRule_updatesRule() {
+ ZenMode ruleMode = newZenMode("rule", ZEN_RULE, false);
+
+ mBackend.updateMode(ruleMode);
+
+ verify(mNm).updateAutomaticZenRule(eq("rule"), eq(ZEN_RULE), eq(true));
+ }
+
+ @Test
+ public void activateMode_manualDnd_setsZenModeImportant() {
+ mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false), null);
+
+ verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+ any(), eq(true));
+ }
+
+ @Test
+ public void activateMode_manualDndWithDuration_setsZenModeImportantWithCondition() {
+ mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false),
+ Duration.ofMinutes(30));
+
+ verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS),
+ eq(ZenModeConfig.toTimeCondition(mContext, 30, 0, true).id),
+ any(),
+ eq(true));
+ }
+
+ @Test
+ public void activateMode_zenRule_setsRuleStateActive() {
+ mBackend.activateMode(newZenMode(ZEN_RULE_ID, ZEN_RULE, false), null);
+
+ verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID),
+ eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_TRUE,
+ Condition.SOURCE_USER_ACTION)));
+ }
+
+ @Test
+ public void activateMode_zenRuleWithDuration_fails() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mBackend.activateMode(newZenMode(ZEN_RULE_ID, ZEN_RULE, false),
+ Duration.ofMinutes(30)));
+ }
+
+ @Test
+ public void deactivateMode_manualDnd_setsZenModeOff() {
+ mBackend.deactivateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, true));
+
+ verify(mNm).setZenMode(eq(ZEN_MODE_OFF), eq(null), any(), eq(true));
+ }
+
+ @Test
+ public void deactivateMode_zenRule_setsRuleStateInactive() {
+ mBackend.deactivateMode(newZenMode(ZEN_RULE_ID, ZEN_RULE, false));
+
+ verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID),
+ eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_FALSE,
+ Condition.SOURCE_USER_ACTION)));
+ }
+
+ @Test
+ public void removeMode_zenRule_deletesRule() {
+ mBackend.removeMode(newZenMode(ZEN_RULE_ID, ZEN_RULE, false));
+
+ verify(mNm).removeAutomaticZenRule(ZEN_RULE_ID, true);
+ }
+
+ @Test
+ public void removeMode_manualDnd_fails() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mBackend.removeMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false)));
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 9f2ab69..d4a4703 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -134,6 +134,7 @@
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
+ Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
@@ -279,5 +280,6 @@
Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
+ Settings.Secure.MANDATORY_BIOMETRICS,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index cb7ac45..6df1c45 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -195,6 +195,7 @@
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
@@ -438,5 +439,6 @@
VALIDATORS.put(Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.MANDATORY_BIOMETRICS, new InclusiveIntegerRangeValidator(0, 1));
}
}
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index 46c847b..f4a3a50 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -24,10 +24,10 @@
<string name="bugreport_updating_wait" msgid="3322151947853929470">"لطفاً منتظر بمانید..."</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"گزارش مشکل بهزودی در تلفن نشان داده میشود"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"برای همرسانی گزارش اشکالتان، انتخاب کنید"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"برای همرسانی گزارش اشکال، ضربه بزنید"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"برای همرسانی گزارش اشکال، تکضرب بزنید"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"انتخاب کنید تا گزارش اشکالتان بدون نماگرفت به اشتراک گذاشته شود یا منتظر بمانید گرفتن عکس از صفحهنمایش تمام شود"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"برای همرسانی گزارش مشکل بدون نماگرفت، ضربه بزنید یا صبر کنید تا نماگرفت گرفته شود."</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"برای همرسانی گزارش مشکل بدون نماگرفت، ضربه بزنید یا صبر کنید تا نماگرفت گرفته شود."</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"برای همرسانی گزارش مشکل بدون نماگرفت، تکضرب بزنید یا صبر کنید تا نماگرفت گرفته شود."</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"برای همرسانی گزارش مشکل بدون نماگرفت، تکضرب بزنید یا صبر کنید تا نماگرفت گرفته شود."</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"گزارشهای اشکال حاوی دادههایی از فایلهای مختلف گزارش سیستم هستند، که ممکن است حاوی دادههای حساس شما (از قبیل دادههای استفاده از برنامه و مکان) باشند. گزارشهای اشکال را فقط با افراد و برنامههایی که به آنها اعتماد دارید بهاشتراک بگذارید."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"دوباره نشان داده نشود"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"گزارش اشکال"</string>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index ce39385..876bd9d 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -25,9 +25,9 @@
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Feilrapporten vises snart på telefonen"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Velg for å dele feilrapporten"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Trykk for å dele feilrapporten"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Velg for å dele feilrapporten uten noen skjermdump, eller vent til skjermdumpen er klar"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Trykk for å dele feilrapporten uten noen skjermdump, eller vent til skjermdumpen er klar"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Trykk for å dele feilrapporten uten noen skjermdump, eller vent til skjermdumpen er klar"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Velg for å dele feilrapporten uten noen skjermbilde, eller vent til skjermbildet er klar"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Trykk for å dele feilrapporten uten noen skjermbilde, eller vent til skjermbildet er klar"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Trykk for å dele feilrapporten uten noen skjermbilde, eller vent til skjermbildet er klar"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Feilrapporter inneholder data fra systemets forskjellige loggfiler. Dette kan inkludere data du ser på som sensitiv (for eksempel appbruk og posisjonsdata). Du bør bare dele feilrapporter med folk og apper du stoler på."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Ikke vis igjen"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Feilrapporter"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index fde7c2c..bd7c9a0 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -98,13 +98,66 @@
],
}
-// Tests where robolectric failed at runtime. (go/multivalent-tests)
+// Tests where robolectric failed at runtime. (go/central-multivalent)
filegroup {
name: "SystemUI-tests-broken-robofiles-run",
srcs: [
+ "tests/src/**/systemui/ExpandHelperTest.java",
+ "tests/src/**/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java",
+ "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java",
+ "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java",
+ "tests/src/**/systemui/screenshot/appclips/AppClipsActivityTest.java",
+ "tests/src/**/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java",
+ "tests/src/**/systemui/screenshot/appclips/AppClipsViewModelTest.java",
+ "tests/src/**/systemui/appops/AppOpsControllerTest.java",
+ "tests/src/**/systemui/biometrics/BiometricNotificationServiceTest.java",
+ "tests/src/**/systemui/bluetooth/BroadcastDialogDelegateTest.java",
+ "tests/src/**/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java",
+ "tests/src/**/systemui/communal/data/backup/CommunalBackupHelperTest.kt",
+ "tests/src/**/systemui/controls/ui/ControlsPopupMenuTest.kt",
+ "tests/src/**/systemui/classifier/DistanceClassifierTest.java",
+ "tests/src/**/systemui/doze/DozeScreenBrightnessTest.java",
+ "tests/src/**/systemui/doze/DozeSensorsTest.java",
+ "tests/src/**/systemui/doze/DozeTriggersTest.java",
+ "tests/src/**/systemui/classifier/FalsingDataProviderTest.java",
+ "tests/src/**/systemui/screenshot/ImageExporterTest.java",
+ "tests/src/**/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt",
+ "tests/src/**/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt",
+ "tests/src/**/systemui/logcat/LogAccessDialogActivityTest.java",
+ "tests/src/**/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt",
+ "tests/src/**/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt",
+ "tests/src/**/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java",
+ "tests/src/**/systemui/accessibility/floatingmenu/MenuViewLayerTest.java",
+ "tests/src/**/systemui/accessibility/floatingmenu/MenuViewTest.java",
+ "tests/src/**/systemui/classifier/PointerCountClassifierTest.java",
+ "tests/src/**/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt",
+ "tests/src/**/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java",
+ "tests/src/**/systemui/screenrecord/RecordingControllerTest.java",
+ "tests/src/**/systemui/screenshot/RequestProcessorTest.kt",
+ "tests/src/**/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt",
+ "tests/src/**/systemui/screenshot/SaveImageInBackgroundTaskTest.kt",
+ "tests/src/**/systemui/screenshot/scroll/ScrollCaptureClientTest.java",
+ "tests/src/**/systemui/accessibility/SecureSettingsContentObserverTest.java",
+ "tests/src/**/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt",
+ "tests/src/**/systemui/qs/external/TileServicesTest.java",
+ "tests/src/**/systemui/ambient/touch/TouchMonitorTest.java",
+ "tests/src/**/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java",
+ "tests/src/**/systemui/accessibility/WindowMagnificationSettingsTest.java",
+ "tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt",
+ "tests/src/**/systemui/CameraProtectionLoaderImplTest.kt",
+ "tests/src/**/systemui/DependencyTest.java",
+ "tests/src/**/systemui/InitControllerTest.java",
+ "tests/src/**/systemui/SliceBroadcastRelayHandlerTest.java",
+ "tests/src/**/systemui/SystemUIApplicationTest.kt",
+ "tests/src/**/systemui/SysUICutoutProviderTest.kt",
+ "tests/src/**/keyguard/ActiveUnlockConfigTest.kt",
+ "tests/src/**/keyguard/AdminSecondaryLockScreenControllerTest.java",
+ "tests/src/**/keyguard/KeyguardClockAccessibilityDelegateTest.java",
+ "tests/src/**/keyguard/KeyguardStatusViewControllerTest.java",
"tests/src/**/systemui/accessibility/AccessibilityButtonModeObserverTest.java",
"tests/src/**/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java",
"tests/src/**/systemui/accessibility/FullscreenMagnificationControllerTest.java",
+ "tests/src/**/systemui/accessibility/MagnificationTest.java",
"tests/src/**/systemui/accessibility/WindowMagnificationAnimationControllerTest.java",
"tests/src/**/systemui/animation/FontInterpolatorTest.kt",
"tests/src/**/systemui/animation/TextAnimatorTest.kt",
@@ -160,6 +213,7 @@
"tests/src/**/systemui/statusbar/KeyboardShortcutsTest.java",
"tests/src/**/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt",
"tests/src/**/systemui/statusbar/notification/AssistantFeedbackControllerTest.java",
+ "tests/src/**/systemui/statusbar/notification/PropertyAnimatorTest.java",
"tests/src/**/systemui/statusbar/notification/collection/NotifCollectionTest.java",
"tests/src/**/systemui/statusbar/notification/collection/NotificationEntryTest.java",
"tests/src/**/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt",
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e03ac3d..795b395 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -8,6 +8,7 @@
acul@google.com
adamcohen@google.com
aioana@google.com
+alexchau@google.com
alexflo@google.com
andonian@google.com
amiko@google.com
@@ -89,8 +90,10 @@
rgl@google.com
roosa@google.com
saff@google.com
+samcackett@google.com
santie@google.com
shanh@google.com
+silvajordan@google.com
snoeberger@google.com
spdonghao@google.com
steell@google.com
@@ -103,6 +106,7 @@
tracyzhou@google.com
tsuji@google.com
twickham@google.com
+uwaisashraf@google.com
vadimt@google.com
valiiftime@google.com
vanjan@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index deab818..16dd4e5 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -81,7 +81,7 @@
]
}
],
-
+
"postsubmit": [
{
// Permission indicators
@@ -93,7 +93,7 @@
]
}
],
-
+
// v2/sysui/suite/test-mapping-sysui-screenshot-test
"sysui-screenshot-test": [
{
@@ -131,7 +131,7 @@
]
}
],
-
+
// v2/sysui/suite/test-mapping-sysui-screenshot-test-staged
"sysui-screenshot-test-staged": [
{
@@ -156,5 +156,13 @@
}
]
}
+ ],
+ "sysui-robo-test": [
+ {
+ "name": "SystemUIGoogleRoboRNGTests"
+ },
+ {
+ "name": "SystemUIGoogleRobo2RNGTests"
+ }
]
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-nb/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-nb/strings.xml
index a3e7ab0..0983052 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-nb/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-nb/strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_menu_service_name" msgid="730136711554740131">"Tilgjengelighetsmeny"</string>
- <string name="accessibility_menu_intro" msgid="3164193281544042394">"Med tilgjengelighetsmenyen får du en stor meny på skjermen for å kontrollere enheten. Du kan låse enheten, kontrollere volum og lysstyrke, ta skjermdumper med mer."</string>
+ <string name="accessibility_menu_intro" msgid="3164193281544042394">"Med tilgjengelighetsmenyen får du en stor meny på skjermen for å kontrollere enheten. Du kan låse enheten, kontrollere volum og lysstyrke, ta skjermbilder med mer."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistent"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Assistent"</string>
<string name="a11y_settings_label" msgid="3977714687248445050">"Tilgjengelighetsinnstillinger"</string>
@@ -13,14 +13,14 @@
<string name="quick_settings_label" msgid="2999117381487601865">"Hurtiginnstillinger"</string>
<string name="notifications_label" msgid="6829741046963013567">"Varsler"</string>
<string name="screenshot_label" msgid="863978141223970162">"Skjermdump"</string>
- <string name="screenshot_utterance" msgid="1430760563401895074">"Ta skjermdump"</string>
+ <string name="screenshot_utterance" msgid="1430760563401895074">"Ta skjermbilde"</string>
<string name="volume_up_label" msgid="8592766918780362870">"Volum opp"</string>
<string name="volume_down_label" msgid="8574981863656447346">"Volum ned"</string>
<string name="brightness_up_label" msgid="8010753822854544846">"Lysstyrke opp"</string>
<string name="brightness_down_label" msgid="7115662941913272072">"Lysstyrke ned"</string>
<string name="previous_button_content_description" msgid="840869171117765966">"Gå til forrige skjerm"</string>
<string name="next_button_content_description" msgid="6810058269847364406">"Gå til neste skjerm"</string>
- <string name="accessibility_menu_description" msgid="4458354794093858297">"Med tilgjengelighetsmenyen får du en stor meny på skjermen for å kontrollere enheten. Du kan låse enheten, kontrollere volum og lysstyrke, ta skjermdumper med mer."</string>
+ <string name="accessibility_menu_description" msgid="4458354794093858297">"Med tilgjengelighetsmenyen får du en stor meny på skjermen for å kontrollere enheten. Du kan låse enheten, kontrollere volum og lysstyrke, ta skjermbilder med mer."</string>
<string name="accessibility_menu_summary" msgid="340071398148208130">"Kontroller enheten med en stor meny"</string>
<string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Innstillinger for Tilgjengelighetsmeny"</string>
<string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Store knapper"</string>
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index d201071..0861454 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -73,6 +73,13 @@
}
flag {
+ name: "redesign_magnification_window_size"
+ namespace: "accessibility"
+ description: "Redesigns the window magnification magnifier sizes provided in the settings panel."
+ bug: "288056772"
+}
+
+flag {
name: "save_and_restore_magnification_settings_buttons"
namespace: "accessibility"
description: "Saves the selected button status in magnification settings and restore the status when revisiting the same smallest screen DP."
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 0fdcc7a..90885ab 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -421,10 +421,13 @@
}
flag {
- name: "fast_unlock_transition"
+ name: "faster_unlock_transition"
namespace: "systemui"
description: "Faster wallpaper unlock transition"
bug: "298186160"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -864,6 +867,13 @@
}
flag {
+ name: "keyboard_touchpad_contextual_education"
+ namespace: "systemui"
+ description: "Allow showing education for physical keyboard and touchpad"
+ bug: "317496783"
+}
+
+flag {
name: "dream_overlay_bouncer_swipe_direction_filtering"
namespace: "systemui"
description: "do not initiate bouncer swipe when the direction is opposite of the expansion"
@@ -1011,13 +1021,6 @@
}
flag {
- name: "glanceable_hub_fullscreen_swipe"
- namespace: "systemui"
- description: "Increase swipe area for gestures to bring in glanceable hub"
- bug: "339665673"
-}
-
-flag {
name: "glanceable_hub_shortcut_button"
namespace: "systemui"
description: "Shows a button over the dream and lock screen to open the glanceable hub"
@@ -1095,3 +1098,23 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "media_lockscreen_launch_animation"
+ namespace : "systemui"
+ description : "Enable the origin launch animation for UMO when opening on top of lockscreen."
+ bug : "346865769"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "expand_heads_up_on_inline_reply"
+ namespace: "systemui"
+ description: "Expands heads up notification when users clicks reply button and activate inline reply"
+ bug: "346976443"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 87bac83..2ed0f6c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -7,6 +7,7 @@
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -31,15 +32,12 @@
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.CommunalSwipeDetector
-import com.android.compose.animation.scene.DefaultSwipeDetector
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.FixedSizeEdgeDetector
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
@@ -51,7 +49,6 @@
import com.android.compose.animation.scene.transitions
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.Flags
-import com.android.systemui.Flags.glanceableHubFullscreenSwipe
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -59,9 +56,10 @@
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
-import com.android.systemui.res.R
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
+import kotlin.time.DurationUnit
object Communal {
object Elements {
@@ -91,6 +89,10 @@
spec = tween(durationMillis = 250)
fade(AllElements)
}
+ to(CommunalScenes.Blank, key = CommunalTransitionKeys.SimpleFade) {
+ spec = tween(durationMillis = TO_GONE_DURATION.toInt(DurationUnit.MILLISECONDS))
+ fade(AllElements)
+ }
to(CommunalScenes.Communal) {
spec = tween(durationMillis = 1000)
translate(Communal.Elements.Grid, Edge.Right)
@@ -131,6 +133,9 @@
fade(Communal.Elements.Grid)
}
}
+ // Disable horizontal overscroll. If the scene is overscrolled too soon after showing, this
+ // can lead to inconsistent KeyguardState changes.
+ overscroll(CommunalScenes.Communal, Orientation.Horizontal) {}
}
/**
@@ -181,25 +186,11 @@
onDispose { viewModel.setTransitionState(null) }
}
- val swipeSourceDetector =
- if (glanceableHubFullscreenSwipe()) {
- detector
- } else {
- FixedSizeEdgeDetector(dimensionResource(id = R.dimen.communal_gesture_initiation_width))
- }
-
- val swipeDetector =
- if (glanceableHubFullscreenSwipe()) {
- detector
- } else {
- DefaultSwipeDetector
- }
-
SceneTransitionLayout(
state = state,
modifier = modifier.fillMaxSize(),
- swipeSourceDetector = swipeSourceDetector,
- swipeDetector = swipeDetector,
+ swipeSourceDetector = detector,
+ swipeDetector = detector,
) {
scene(
CommunalScenes.Blank,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 18085ab..1ce51af 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -27,8 +27,10 @@
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
+import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
+import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject
@@ -41,64 +43,88 @@
private val interactionHandler: SmartspaceInteractionHandler,
private val dialogFactory: SystemUIDialogFactory,
private val lockSection: LockSection,
+ private val bottomAreaSection: BottomAreaSection,
private val ambientStatusBarSection: AmbientStatusBarSection,
+ private val communalPopupSection: CommunalPopupSection,
) {
+
@Composable
fun SceneScope.Content(modifier: Modifier = Modifier) {
- Layout(
- modifier = modifier.fillMaxSize(),
- content = {
- Box(modifier = Modifier.fillMaxSize()) {
- with(ambientStatusBarSection) {
- AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+ CommunalTouchableSurface(viewModel = viewModel, modifier = modifier) {
+ Layout(
+ modifier = Modifier.fillMaxSize(),
+ content = {
+ Box(modifier = Modifier.fillMaxSize()) {
+ with(communalPopupSection) { Popup() }
+ with(ambientStatusBarSection) {
+ AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+ }
+ CommunalHub(
+ viewModel = viewModel,
+ interactionHandler = interactionHandler,
+ dialogFactory = dialogFactory,
+ modifier = Modifier.element(Communal.Elements.Grid)
+ )
}
- CommunalHub(
- viewModel = viewModel,
- interactionHandler = interactionHandler,
- dialogFactory = dialogFactory,
- modifier = Modifier.element(Communal.Elements.Grid)
+ with(lockSection) {
+ LockIcon(
+ overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
+ modifier = Modifier.element(Communal.Elements.LockIcon)
+ )
+ }
+ with(bottomAreaSection) {
+ IndicationArea(
+ Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
+ )
+ }
+ }
+ ) { measurables, constraints ->
+ val communalGridMeasurable = measurables[0]
+ val lockIconMeasurable = measurables[1]
+ val bottomAreaMeasurable = measurables[2]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val bottomAreaPlaceable =
+ bottomAreaMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight =
+ (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
+ )
+ )
+
+ val communalGridPlaceable =
+ communalGridMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ communalGridPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ bottomAreaPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - bottomAreaPlaceable.height,
)
}
- with(lockSection) {
- LockIcon(
- overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
- modifier = Modifier.element(Communal.Elements.LockIcon)
- )
- }
- }
- ) { measurables, constraints ->
- val communalGridMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
-
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
-
- val communalGridPlaceable =
- communalGridMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- communalGridPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index fbfe050..9c2127c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -25,7 +25,6 @@
import android.widget.RemoteViews
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
@@ -49,13 +48,16 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
@@ -68,19 +70,21 @@
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.outlined.Edit
-import androidx.compose.material.icons.outlined.TouchApp
import androidx.compose.material.icons.outlined.Widgets
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonColors
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
+import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
@@ -93,14 +97,16 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
-import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.key.onPreviewKeyEvent
-import androidx.compose.ui.input.pointer.motionEventSpy
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.boundsInWindow
@@ -120,14 +126,11 @@
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.times
import androidx.compose.ui.viewinterop.AndroidView
-import androidx.compose.ui.window.Popup
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.layout.WindowMetricsCalculator
import com.android.compose.animation.Easings.Emphasized
@@ -138,7 +141,6 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.ui.compose.Dimensions.CardOutlineWidth
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
@@ -146,14 +148,13 @@
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.ui.viewmodel.PopupType
import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import kotlinx.coroutines.launch
-@OptIn(ExperimentalComposeUiApi::class)
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
@Composable
fun CommunalHub(
modifier: Modifier = Modifier,
@@ -166,7 +167,6 @@
) {
val communalContent by
viewModel.communalContent.collectAsStateWithLifecycle(initialValue = emptyList())
- val currentPopup by viewModel.currentPopup.collectAsStateWithLifecycle(initialValue = null)
var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var toolbarSize: IntSize? by remember { mutableStateOf(null) }
var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
@@ -208,43 +208,29 @@
}
.thenIf(!viewModel.isEditMode && !isEmptyState) {
Modifier.pointerInput(
- gridState,
- contentOffset,
- communalContent,
- gridCoordinates
- ) {
- detectLongPressGesture { offset ->
- // Deduct both grid offset relative to its container and content
- // offset.
- val adjustedOffset =
- gridCoordinates?.let {
- offset - it.positionInWindow() - contentOffset
- }
- val index =
- adjustedOffset?.let { firstIndexAtOffset(gridState, it) }
- // Display the button only when the gesture initiates from widgets,
- // the CTA tile, or an empty area on the screen. UMO/smartspace have
- // their own long-press handlers. To prevent user confusion, we
- // should
- // not display this button.
- if (
- index == null ||
- communalContent[index].isWidgetContent() ||
- communalContent[index] is
- CommunalContentModel.CtaTileInViewMode
- ) {
- viewModel.onShowCustomizeWidgetButton()
+ gridState,
+ contentOffset,
+ communalContent,
+ gridCoordinates
+ ) {
+ detectLongPressGesture { offset ->
+ // Deduct both grid offset relative to its container and content
+ // offset.
+ val adjustedOffset =
+ gridCoordinates?.let {
+ offset - it.positionInWindow() - contentOffset
}
- val key =
- index?.let { keyAtIndexIfEditable(communalContent, index) }
+ val index = adjustedOffset?.let { firstIndexAtOffset(gridState, it) }
+ val key = index?.let { keyAtIndexIfEditable(communalContent, index) }
+ // Handle long-click on widgets and set the selected index
+ // correctly. We only handle widgets here because long click on
+ // empty spaces is handled by CommunalPopupSection.
+ if (key != null) {
+ viewModel.onLongClick()
viewModel.setSelectedKey(key)
}
}
- .onPreviewKeyEvent {
- onKeyEvent(viewModel)
- false
- }
- .motionEventSpy { onMotionEvent(viewModel) }
+ }
},
) {
AccessibilityContainer(viewModel) {
@@ -337,22 +323,6 @@
)
}
}
- if (currentPopup == PopupType.CtaTile) {
- PopupOnDismissCtaTile(viewModel::onHidePopup)
- }
-
- AnimatedVisibility(
- visible = currentPopup == PopupType.CustomizeWidgetButton,
- modifier = Modifier.fillMaxSize()
- ) {
- ButtonToEditWidgets(
- onClick = {
- viewModel.onHidePopup()
- viewModel.onOpenWidgetEditor(selectedKey.value)
- },
- onHide = { viewModel.onHidePopup() }
- )
- }
if (viewModel is CommunalViewModel && dialogFactory != null) {
val isEnableWidgetDialogShowing by
@@ -378,15 +348,75 @@
onCancel = viewModel::onEnableWorkProfileDialogCancel
)
}
+
+ if (viewModel is CommunalEditModeViewModel) {
+ val showBottomSheet by viewModel.showDisclaimer.collectAsStateWithLifecycle(false)
+
+ if (showBottomSheet) {
+ val scope = rememberCoroutineScope()
+ val sheetState = rememberModalBottomSheetState()
+ val colors = LocalAndroidColorScheme.current
+
+ ModalBottomSheet(
+ onDismissRequest = viewModel::onDisclaimerDismissed,
+ sheetState = sheetState,
+ dragHandle = null,
+ containerColor = colors.surfaceContainer,
+ ) {
+ DisclaimerBottomSheetContent {
+ scope
+ .launch { sheetState.hide() }
+ .invokeOnCompletion {
+ if (!sheetState.isVisible) {
+ viewModel.onDisclaimerDismissed()
+ }
+ }
+ }
+ }
+ }
+ }
}
}
-private fun onKeyEvent(viewModel: BaseCommunalViewModel) {
- viewModel.signalUserInteraction()
-}
+@Composable
+private fun DisclaimerBottomSheetContent(onButtonClicked: () -> Unit) {
+ val colors = LocalAndroidColorScheme.current
-private fun onMotionEvent(viewModel: BaseCommunalViewModel) {
- viewModel.signalUserInteraction()
+ Column(
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 32.dp, vertical = 24.dp),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Widgets,
+ contentDescription = null,
+ tint = colors.primary,
+ modifier = Modifier.size(32.dp)
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = stringResource(R.string.communal_widgets_disclaimer_title),
+ style = MaterialTheme.typography.headlineMedium,
+ color = colors.onSurface,
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = stringResource(R.string.communal_widgets_disclaimer_text),
+ color = colors.onSurfaceVariant,
+ )
+ Button(
+ modifier =
+ Modifier.padding(horizontal = 26.dp, vertical = 16.dp)
+ .widthIn(min = 200.dp)
+ .heightIn(min = 56.dp),
+ onClick = { onButtonClicked() }
+ ) {
+ Text(
+ stringResource(R.string.communal_widgets_disclaimer_button),
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+ }
}
/**
@@ -516,7 +546,7 @@
}
} else {
CommunalContent(
- modifier = cardModifier.animateItemPlacement(),
+ modifier = cardModifier.animateItem(),
model = list[index],
viewModel = viewModel,
size = size,
@@ -602,10 +632,14 @@
removeEnabled: Boolean,
onRemoveClicked: () -> Unit,
setToolbarSize: (toolbarSize: IntSize) -> Unit,
- setRemoveButtonCoordinates: (coordinates: LayoutCoordinates) -> Unit,
+ setRemoveButtonCoordinates: (coordinates: LayoutCoordinates?) -> Unit,
onOpenWidgetPicker: () -> Unit,
onEditDone: () -> Unit
) {
+ if (!removeEnabled) {
+ // Clear any existing coordinates when remove is not enabled.
+ setRemoveButtonCoordinates(null)
+ }
val removeButtonAlpha: Float by
animateFloatAsState(
targetValue = if (removeEnabled) 1f else 0.5f,
@@ -645,7 +679,13 @@
contentPadding = Dimensions.ButtonPadding,
modifier =
Modifier.graphicsLayer { alpha = removeButtonAlpha }
- .onGloballyPositioned { setRemoveButtonCoordinates(it) }
+ .onGloballyPositioned {
+ // It's possible for this callback to fire after remove has been
+ // disabled. Check enabled state before setting.
+ if (removeEnabled) {
+ setRemoveButtonCoordinates(it)
+ }
+ }
) {
Row(
horizontalArrangement =
@@ -738,107 +778,6 @@
}
@Composable
-private fun AnimatedVisibilityScope.ButtonToEditWidgets(
- onClick: () -> Unit,
- onHide: () -> Unit,
-) {
- Popup(
- alignment = Alignment.TopCenter,
- offset = IntOffset(0, 40),
- onDismissRequest = onHide,
- ) {
- val colors = LocalAndroidColorScheme.current
- Button(
- modifier =
- Modifier.height(56.dp)
- .graphicsLayer { transformOrigin = TransformOrigin(0f, 0f) }
- .animateEnterExit(
- enter =
- fadeIn(
- initialAlpha = 0f,
- animationSpec = tween(durationMillis = 83, easing = LinearEasing)
- ),
- exit =
- fadeOut(
- animationSpec =
- tween(
- durationMillis = 83,
- delayMillis = 167,
- easing = LinearEasing
- )
- )
- )
- .background(colors.secondary, RoundedCornerShape(50.dp)),
- onClick = onClick,
- ) {
- Row(
- modifier =
- Modifier.animateEnterExit(
- enter =
- fadeIn(
- animationSpec =
- tween(
- durationMillis = 167,
- delayMillis = 83,
- easing = LinearEasing
- )
- ),
- exit =
- fadeOut(
- animationSpec = tween(durationMillis = 167, easing = LinearEasing)
- )
- )
- ) {
- Icon(
- imageVector = Icons.Outlined.Widgets,
- contentDescription = stringResource(R.string.button_to_configure_widgets_text),
- tint = colors.onSecondary,
- modifier = Modifier.size(20.dp)
- )
- Spacer(modifier = Modifier.size(8.dp))
- Text(
- text = stringResource(R.string.button_to_configure_widgets_text),
- style = MaterialTheme.typography.titleSmall,
- color = colors.onSecondary
- )
- }
- }
- }
-}
-
-@Composable
-private fun PopupOnDismissCtaTile(onHidePopup: () -> Unit) {
- Popup(
- alignment = Alignment.TopCenter,
- offset = IntOffset(0, 40),
- onDismissRequest = onHidePopup
- ) {
- val colors = LocalAndroidColorScheme.current
- Row(
- modifier =
- Modifier.height(56.dp)
- .background(colors.secondary, RoundedCornerShape(50.dp))
- .padding(16.dp),
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- imageVector = Icons.Outlined.TouchApp,
- contentDescription = stringResource(R.string.popup_on_dismiss_cta_tile_text),
- tint = colors.onSecondary,
- modifier = Modifier.size(20.dp)
- )
- Spacer(modifier = Modifier.size(8.dp))
- Text(
- text = stringResource(R.string.popup_on_dismiss_cta_tile_text),
- style = MaterialTheme.typography.titleSmall,
- color = colors.onSecondary,
- )
- }
- }
-}
-
-@Composable
private fun filledButtonColors(): ButtonColors {
val colors = LocalAndroidColorScheme.current
return ButtonDefaults.buttonColors(
@@ -885,13 +824,26 @@
/** Creates an empty card used to highlight a particular spot on the grid. */
@Composable
-fun HighlightedItem(modifier: Modifier = Modifier) {
- Card(
- modifier = modifier,
- colors = CardDefaults.cardColors(containerColor = Color.Transparent),
- border = BorderStroke(CardOutlineWidth, LocalAndroidColorScheme.current.tertiaryFixed),
- shape = RoundedCornerShape(16.dp)
- ) {}
+fun HighlightedItem(modifier: Modifier = Modifier, alpha: Float = 1.0f) {
+ val brush = SolidColor(LocalAndroidColorScheme.current.primaryFixed)
+ Box(
+ modifier =
+ // drawBehind lets us draw outside the bounds of the widgets so that we don't need to
+ // resize grid items to account for the border.
+ modifier.drawBehind {
+ // 8dp of padding between the widget and the highlight on every side.
+ val padding = 8.dp.toPx()
+ drawRoundRect(
+ brush,
+ alpha = alpha,
+ topLeft = Offset(-padding, -padding),
+ size =
+ Size(width = size.width + padding * 2, height = size.height + padding * 2),
+ cornerRadius = CornerRadius(37.dp.toPx()),
+ style = Stroke(width = 3.dp.toPx())
+ )
+ }
+ )
}
/** Presents a CTA tile at the end of the grid, to customize the hub. */
@@ -911,7 +863,8 @@
shape = RoundedCornerShape(68.dp, 34.dp, 68.dp, 34.dp)
) {
Column(
- modifier = Modifier.fillMaxSize().padding(vertical = 38.dp, horizontal = 70.dp),
+ modifier = Modifier.fillMaxSize().padding(vertical = 32.dp, horizontal = 50.dp),
+ verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
@@ -922,41 +875,43 @@
Spacer(modifier = Modifier.size(6.dp))
Text(
text = stringResource(R.string.cta_label_to_edit_widget),
- style = MaterialTheme.typography.titleMedium,
- textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.titleLarge,
+ fontSize = nonScalableTextSize(22.dp),
+ lineHeight = nonScalableTextSize(28.dp),
)
- Spacer(modifier = Modifier.size(20.dp))
+ Spacer(modifier = Modifier.size(16.dp))
Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().height(56.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally),
) {
OutlinedButton(
+ modifier = Modifier.fillMaxHeight(),
colors =
ButtonDefaults.buttonColors(
contentColor = colors.onPrimary,
),
border = BorderStroke(width = 1.0.dp, color = colors.primaryContainer),
- contentPadding = Dimensions.ButtonPadding,
+ contentPadding = PaddingValues(26.dp, 8.dp),
onClick = viewModel::onDismissCtaTile,
) {
Text(
text = stringResource(R.string.cta_tile_button_to_dismiss),
- fontSize = 12.sp,
+ fontSize = nonScalableTextSize(14.dp),
)
}
- Spacer(modifier = Modifier.size(14.dp))
Button(
+ modifier = Modifier.fillMaxHeight(),
colors =
ButtonDefaults.buttonColors(
containerColor = colors.primaryContainer,
contentColor = colors.onPrimaryContainer,
),
- contentPadding = Dimensions.ButtonPadding,
+ contentPadding = PaddingValues(26.dp, 8.dp),
onClick = viewModel::onOpenWidgetEditor
) {
Text(
text = stringResource(R.string.cta_tile_button_to_open_widget_editor),
- fontSize = 12.sp,
+ fontSize = nonScalableTextSize(14.dp),
)
}
}
@@ -1269,6 +1224,13 @@
}
/**
+ * Text size converted from dp value to the equivalent sp value using the current screen density,
+ * ensuring it does not scale with the font size setting.
+ */
+@Composable
+private fun nonScalableTextSize(sizeInDp: Dp) = with(LocalDensity.current) { sizeInDp.toSp() }
+
+/**
* Returns the `contentPadding` of the grid. Use the vertical padding to push the grid content area
* below the toolbar and let the grid take the max size. This ensures the item can be dragged
* outside the grid over the toolbar, without part of it getting clipped by the container.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt
new file mode 100644
index 0000000..3707a87
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalTouchableSurface.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.onPreviewKeyEvent
+import androidx.compose.ui.input.pointer.motionEventSpy
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+
+@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
+@Composable
+fun CommunalTouchableSurface(
+ viewModel: CommunalViewModel,
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit,
+) {
+
+ val interactionSource = remember { MutableInteractionSource() }
+
+ Box(
+ modifier =
+ modifier
+ .combinedClickable(
+ onLongClick = viewModel::onLongClick,
+ onClick = viewModel::onClick,
+ interactionSource = interactionSource,
+ indication = null,
+ )
+ .onPreviewKeyEvent {
+ viewModel.signalUserInteraction()
+ false
+ }
+ .motionEventSpy { viewModel.signalUserInteraction() }
+ ) {
+ content()
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 33d2cc4..4eae14b3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -16,10 +16,9 @@
package com.android.systemui.communal.ui.compose
-import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
+import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.foundation.gestures.scrollBy
@@ -34,13 +33,10 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.input.pointer.pointerInteropFilter
-import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.unit.toSize
@@ -107,8 +103,7 @@
get() =
draggingItemLayoutInfo?.let { item ->
draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset()
- }
- ?: Offset.Zero
+ } ?: Offset.Zero
private val draggingItemLayoutInfo: LazyGridItemInfo?
get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
@@ -238,7 +233,6 @@
}
/** Wrap LazyGrid item with additional modifier needed for drag and drop. */
-@OptIn(ExperimentalComposeUiApi::class)
@ExperimentalFoundationApi
@Composable
fun LazyGridItemScope.DraggableItem(
@@ -267,25 +261,21 @@
alpha = itemAlpha
}
} else {
- Modifier.animateItemPlacement()
+ Modifier.animateItem()
}
+ // Animate the highlight alpha manually as alpha modifier (and AnimatedVisibility) clips the
+ // widget to bounds, which cuts off the highlight as we are drawing outside the widget bounds.
+ val alpha by
+ animateFloatAsState(
+ targetValue =
+ if ((dragging || selected) && !dragDropState.isDraggingToRemove) 1f else 0f,
+ animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
+ label = "Widget outline alpha"
+ )
+
Box(modifier) {
+ HighlightedItem(Modifier.matchParentSize(), alpha = alpha)
Box(draggingModifier) { content(dragging) }
- AnimatedVisibility(
- modifier =
- Modifier.matchParentSize()
- // Avoid taking focus away from the content when using explore-by-touch with
- // accessibility tools.
- .clearAndSetSemantics {}
- // Do not consume motion events in the highlighted item and pass them down to
- // the content.
- .pointerInteropFilter { false },
- visible = (dragging || selected) && !dragDropState.isDraggingToRemove,
- enter = fadeIn(),
- exit = fadeOut()
- ) {
- HighlightedItem(Modifier.matchParentSize())
- }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
new file mode 100644
index 0000000..1ea73e1
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose.section
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.AnimatedVisibilityScope
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.TouchApp
+import androidx.compose.material.icons.outlined.Widgets
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Popup
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.ui.viewmodel.PopupType
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class CommunalPopupSection
+@Inject
+constructor(
+ private val viewModel: CommunalViewModel,
+) {
+
+ @Composable
+ fun Popup() {
+ val currentPopup by viewModel.currentPopup.collectAsStateWithLifecycle(initialValue = null)
+
+ if (currentPopup == PopupType.CtaTile) {
+ PopupOnDismissCtaTile(viewModel::onHidePopup)
+ }
+
+ AnimatedVisibility(
+ visible = currentPopup == PopupType.CustomizeWidgetButton,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ ButtonToEditWidgets(
+ onClick = {
+ viewModel.onHidePopup()
+ viewModel.onOpenWidgetEditor()
+ },
+ onDismissRequest = {
+ viewModel.onHidePopup()
+ viewModel.setSelectedKey(null)
+ }
+ )
+ }
+ }
+
+ @Composable
+ private fun AnimatedVisibilityScope.ButtonToEditWidgets(
+ onClick: () -> Unit,
+ onDismissRequest: () -> Unit,
+ ) {
+ Popup(
+ alignment = Alignment.TopCenter,
+ offset = IntOffset(0, 40),
+ onDismissRequest = onDismissRequest,
+ ) {
+ val colors = LocalAndroidColorScheme.current
+ Button(
+ modifier =
+ Modifier.height(56.dp)
+ .graphicsLayer { transformOrigin = TransformOrigin(0f, 0f) }
+ .animateEnterExit(
+ enter =
+ fadeIn(
+ initialAlpha = 0f,
+ animationSpec =
+ tween(durationMillis = 83, easing = LinearEasing)
+ ),
+ exit =
+ fadeOut(
+ animationSpec =
+ tween(
+ durationMillis = 83,
+ delayMillis = 167,
+ easing = LinearEasing
+ )
+ )
+ )
+ .background(colors.secondary, RoundedCornerShape(50.dp)),
+ onClick = onClick,
+ ) {
+ Row(
+ modifier =
+ Modifier.animateEnterExit(
+ enter =
+ fadeIn(
+ animationSpec =
+ tween(
+ durationMillis = 167,
+ delayMillis = 83,
+ easing = LinearEasing
+ )
+ ),
+ exit =
+ fadeOut(
+ animationSpec =
+ tween(durationMillis = 167, easing = LinearEasing)
+ )
+ )
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Widgets,
+ contentDescription =
+ stringResource(R.string.button_to_configure_widgets_text),
+ tint = colors.onSecondary,
+ modifier = Modifier.size(20.dp)
+ )
+ Spacer(modifier = Modifier.size(8.dp))
+ Text(
+ text = stringResource(R.string.button_to_configure_widgets_text),
+ style = MaterialTheme.typography.titleSmall,
+ color = colors.onSecondary
+ )
+ }
+ }
+ }
+ }
+
+ @Composable
+ private fun PopupOnDismissCtaTile(onDismissRequest: () -> Unit) {
+ Popup(
+ alignment = Alignment.TopCenter,
+ offset = IntOffset(0, 40),
+ onDismissRequest = onDismissRequest
+ ) {
+ val colors = LocalAndroidColorScheme.current
+ Row(
+ modifier =
+ Modifier.height(56.dp)
+ .background(colors.secondary, RoundedCornerShape(50.dp))
+ .padding(16.dp),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.TouchApp,
+ contentDescription = stringResource(R.string.popup_on_dismiss_cta_tile_text),
+ tint = colors.onSecondary,
+ modifier = Modifier.size(20.dp)
+ )
+ Spacer(modifier = Modifier.size(8.dp))
+ Text(
+ text = stringResource(R.string.popup_on_dismiss_cta_tile_text),
+ style = MaterialTheme.typography.titleSmall,
+ color = colors.onSecondary,
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
index 4555f13..c34fb38 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -19,9 +19,10 @@
package com.android.systemui.keyguard.ui.composable
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@@ -33,12 +34,14 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.pointer.pointerInput
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
/** Container for lockscreen content that handles long-press to bring up the settings menu. */
@Composable
+// TODO(b/344879669): now that it's more generic than long-press, rename it.
fun LockscreenLongPress(
- viewModel: KeyguardLongPressViewModel,
+ viewModel: KeyguardTouchHandlingViewModel,
modifier: Modifier = Modifier,
content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
) {
@@ -50,14 +53,17 @@
Box(
modifier =
modifier
- .combinedClickable(
- enabled = isEnabled,
- onLongClick = viewModel::onLongPress,
- onClick = {},
- interactionSource = interactionSource,
- // Passing null for the indication removes the ripple effect.
- indication = null,
- )
+ .pointerInput(isEnabled) {
+ if (isEnabled) {
+ detectLongPressGesture { viewModel.onLongPress() }
+ }
+ }
+ .pointerInput(Unit) {
+ detectTapGestures(
+ onTap = { viewModel.onClick(it.x, it.y) },
+ onDoubleTap = { viewModel.onDoubleClick() },
+ )
+ }
.pointerInput(settingsMenuBounds) {
awaitEachGesture {
val pointerInputChange = awaitFirstDown()
@@ -65,7 +71,9 @@
viewModel.onTouchedOutside()
}
}
- },
+ }
+ // Passing null for the indication removes the ripple effect.
+ .indication(interactionSource, null)
) {
content(setSettingsMenuBounds)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
index 6b210af..210ca69 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -43,7 +43,7 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
LockscreenLongPress(
- viewModel = viewModel.longPress,
+ viewModel = viewModel.touchHandling,
modifier = modifier,
) { _ ->
Box(modifier.background(Color.Black)) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index a39fa64..0a4c6fd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -72,7 +72,7 @@
val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
LockscreenLongPress(
- viewModel = viewModel.longPress,
+ viewModel = viewModel.touchHandling,
modifier = modifier,
) { onSettingsMenuPlaced ->
Layout(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index c83f62c..065f2a2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -74,7 +74,7 @@
val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
LockscreenLongPress(
- viewModel = viewModel.longPress,
+ viewModel = viewModel.touchHandling,
modifier = modifier,
) { onSettingsMenuPlaced ->
Layout(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 97d5b41..86639fa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -24,6 +24,7 @@
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.DpSize
@@ -183,7 +184,7 @@
indicationController: KeyguardIndicationController,
modifier: Modifier = Modifier,
) {
- val (disposable, setDisposable) = mutableStateOf<DisposableHandle?>(null)
+ val (disposable, setDisposable) = remember { mutableStateOf<DisposableHandle?>(null) }
AndroidView(
factory = { context ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
index 44b0535..15032e0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SettingsMenuSection.kt
@@ -30,8 +30,8 @@
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
@@ -42,7 +42,7 @@
@Inject
constructor(
private val viewModel: KeyguardSettingsMenuViewModel,
- private val longPressViewModel: KeyguardLongPressViewModel,
+ private val touchHandlingViewModel: KeyguardTouchHandlingViewModel,
private val vibratorHelper: VibratorHelper,
private val activityStarter: ActivityStarter,
) {
@@ -69,7 +69,7 @@
KeyguardSettingsViewBinder.bind(
view = this,
viewModel = viewModel,
- longPressViewModel = longPressViewModel,
+ touchHandlingViewModel = touchHandlingViewModel,
rootViewModel = null,
vibratorHelper = vibratorHelper,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 29223ce..2eea2f0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -21,6 +21,7 @@
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
@@ -94,6 +95,7 @@
val HeadsUpNotificationPlaceholder =
ElementKey("HeadsUpNotificationPlaceholder", scenePicker = LowestZIndexScenePicker)
val ShelfSpace = ElementKey("ShelfSpace")
+ val NotificationStackCutoffGuideline = ElementKey("NotificationStackCutoffGuideline")
}
// Expansion fraction thresholds (between 0-1f) at which the corresponding value should be
@@ -232,6 +234,8 @@
// The height of the scrim visible on screen when it is in its resting (collapsed) state.
val minVisibleScrimHeight: () -> Float = { screenHeight - maxScrimTop() }
+ val isClickable by viewModel.isClickable.collectAsStateWithLifecycle()
+
// we are not scrolled to the top unless the scrim is at its maximum offset.
LaunchedEffect(viewModel, scrimOffset) {
snapshotFlow { scrimOffset.value >= 0f }
@@ -327,6 +331,9 @@
)
)
}
+ .thenIf(isClickable) {
+ Modifier.clickable(onClick = { viewModel.onEmptySpaceClicked() })
+ }
) {
// Creates a cutout in the background scrim in the shape of the notifications scrim.
// Only visible when notif scrim alpha < 1, during shade expansion.
@@ -410,18 +417,22 @@
* the notification contents (stack, footer, shelf) should be drawn.
*/
@Composable
-fun NotificationStackCutoffGuideline(
+fun SceneScope.NotificationStackCutoffGuideline(
stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
) {
Spacer(
modifier =
- modifier.fillMaxWidth().height(0.dp).onGloballyPositioned { coordinates ->
- val positionY = coordinates.positionInWindow().y
- debugLog(viewModel) { "STACK cutoff onGloballyPositioned: y=$positionY" }
- stackScrollView.setStackCutoff(positionY)
- }
+ modifier
+ .element(key = Notifications.Elements.NotificationStackCutoffGuideline)
+ .fillMaxWidth()
+ .height(0.dp)
+ .onGloballyPositioned { coordinates ->
+ val positionY = coordinates.positionInWindow().y
+ debugLog(viewModel) { "STACK cutoff onGloballyPositioned: y=$positionY" }
+ stackScrollView.setStackCutoff(positionY)
+ }
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index f62a28c..db98bc8f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -20,7 +20,6 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
@@ -75,7 +74,6 @@
OverlayShade(
modifier = modifier,
viewModel = overlayShadeViewModel,
- panelAlignment = Alignment.TopEnd,
lockscreenContent = lockscreenContent,
) {
Column {
@@ -98,6 +96,12 @@
shadeMode = ShadeMode.Dual,
modifier = Modifier.fillMaxWidth(),
)
+
+ // Communicates the bottom position of the drawable area within the shade to NSSL.
+ NotificationStackCutoffGuideline(
+ stackScrollView = stackScrollView.get(),
+ viewModel = notificationsPlaceholderViewModel,
+ )
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
index 73a624a..aca473d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
@@ -18,8 +18,9 @@
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -39,6 +40,7 @@
viewModel: BrightnessMirrorViewModel,
qsSceneAdapter: QSSceneAdapter,
modifier: Modifier = Modifier,
+ measureFromContainer: Boolean = false,
) {
val isShowing by viewModel.isShowing.collectAsStateWithLifecycle()
val mirrorAlpha by
@@ -47,9 +49,22 @@
label = "alphaAnimationBrightnessMirrorShowing",
)
val mirrorOffsetAndSize by viewModel.locationAndSize.collectAsStateWithLifecycle()
- val offset = IntOffset(0, mirrorOffsetAndSize.yOffset)
+ val yOffset =
+ if (measureFromContainer) {
+ mirrorOffsetAndSize.yOffsetFromContainer
+ } else {
+ mirrorOffsetAndSize.yOffsetFromWindow
+ }
+ val offset = IntOffset(0, yOffset)
- Box(modifier = modifier.fillMaxSize().graphicsLayer { alpha = mirrorAlpha }) {
+ // Use unbounded=true as the full mirror (with paddings and background offset) may be larger
+ // than the space we have (but it will fit, because the brightness slider fits).
+ Box(
+ modifier =
+ modifier.fillMaxHeight().wrapContentWidth(unbounded = true).graphicsLayer {
+ alpha = mirrorAlpha
+ }
+ ) {
QuickSettingsTheme {
// The assumption for using this AndroidView is that there will be only one in view at
// a given time (which is a reasonable assumption). Because `QSSceneAdapter` (actually
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 644040d..2d5d259 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -42,6 +42,7 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
@@ -85,6 +86,7 @@
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
+import com.android.systemui.notifications.ui.composable.NotificationStackCutoffGuideline
import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS
@@ -183,7 +185,11 @@
BrightnessMirror(
viewModel = viewModel.brightnessMirrorViewModel,
- qsSceneAdapter = viewModel.qsSceneAdapter
+ qsSceneAdapter = viewModel.qsSceneAdapter,
+ modifier =
+ Modifier.thenIf(cutoutLocation != CutoutLocation.CENTER) {
+ Modifier.displayCutoutPadding()
+ }
)
val shouldPunchHoleBehindScrim =
@@ -416,5 +422,13 @@
modifier =
Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) },
)
+ NotificationStackCutoffGuideline(
+ stackScrollView = notificationStackScrollView,
+ viewModel = viewModel.notifications,
+ modifier =
+ Modifier.align(Alignment.BottomCenter).navigationBarsPadding().offset {
+ IntOffset(x = 0, y = screenHeight.roundToInt())
+ }
+ )
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index a0d6be9..c066ae5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -82,7 +82,6 @@
) {
OverlayShade(
viewModel = viewModel.overlayShadeViewModel,
- panelAlignment = Alignment.TopEnd,
lockscreenContent = lockscreenContent,
modifier = modifier,
) {
@@ -155,6 +154,7 @@
viewModel = viewModel.tileGridViewModel,
modifier =
Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
+ viewModel.editModeViewModel::startEditing,
)
Button(
onClick = { viewModel.editModeViewModel.startEditing() },
@@ -169,7 +169,7 @@
object Dimensions {
val Padding = 16.dp
val BrightnessSliderHeight = 64.dp
- val GridMaxHeight = 400.dp
+ val GridMaxHeight = 800.dp
}
object Transitions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 5dc833b..e433d32 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -1,10 +1,12 @@
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.transitions
import com.android.systemui.bouncer.ui.composable.Bouncer
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
@@ -42,31 +44,28 @@
// Scene transitions
from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
- from(Scenes.Gone, to = Scenes.NotificationsShade) { goneToNotificationsShadeTransition() }
- from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
- from(
- Scenes.Gone,
- to = Scenes.Shade,
- key = ToSplitShade,
- ) {
- goneToSplitShadeTransition()
+ from(Scenes.Gone, to = Scenes.NotificationsShade) {
+ goneToNotificationsShadeTransition(Edge.Top)
}
- from(
- Scenes.Gone,
- to = Scenes.Shade,
- key = SlightlyFasterShadeCollapse,
- ) {
+ from(Scenes.Gone, to = Scenes.NotificationsShade, key = OpenBottomShade) {
+ goneToNotificationsShadeTransition(Edge.Bottom)
+ }
+ from(Scenes.Gone, to = Scenes.QuickSettingsShade) {
+ goneToQuickSettingsShadeTransition(Edge.Top)
+ }
+ from(Scenes.Gone, to = Scenes.QuickSettingsShade, key = OpenBottomShade) {
+ goneToQuickSettingsShadeTransition(Edge.Bottom)
+ }
+ from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
+ from(Scenes.Gone, to = Scenes.Shade, key = ToSplitShade) { goneToSplitShadeTransition() }
+ from(Scenes.Gone, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
goneToShadeTransition(durationScale = 0.9)
}
from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() }
- from(
- Scenes.Gone,
- to = Scenes.QuickSettings,
- key = SlightlyFasterShadeCollapse,
- ) {
+ from(Scenes.Gone, to = Scenes.QuickSettings, key = SlightlyFasterShadeCollapse) {
goneToQuickSettingsTransition(durationScale = 0.9)
}
- from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() }
+
from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() }
from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
from(Scenes.Lockscreen, to = Scenes.NotificationsShade) {
@@ -76,18 +75,10 @@
lockscreenToQuickSettingsShadeTransition()
}
from(Scenes.Lockscreen, to = Scenes.Shade) { lockscreenToShadeTransition() }
- from(
- Scenes.Lockscreen,
- to = Scenes.Shade,
- key = ToSplitShade,
- ) {
+ from(Scenes.Lockscreen, to = Scenes.Shade, key = ToSplitShade) {
lockscreenToSplitShadeTransition()
}
- from(
- Scenes.Lockscreen,
- to = Scenes.Shade,
- key = SlightlyFasterShadeCollapse,
- ) {
+ from(Scenes.Lockscreen, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
lockscreenToShadeTransition(durationScale = 0.9)
}
from(Scenes.Lockscreen, to = Scenes.QuickSettings) { lockscreenToQuickSettingsTransition() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
index 48ec198..fb41374 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
@@ -16,10 +16,12 @@
package com.android.systemui.scene.ui.composable.transitions
+import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
fun TransitionBuilder.goneToNotificationsShadeTransition(
+ edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
- toNotificationsShadeTransition(durationScale)
+ toNotificationsShadeTransition(edge, durationScale)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsShadeTransition.kt
index 225ca4e..8a03e29 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToQuickSettingsShadeTransition.kt
@@ -16,10 +16,12 @@
package com.android.systemui.scene.ui.composable.transitions
+import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
fun TransitionBuilder.goneToQuickSettingsShadeTransition(
+ edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
- toQuickSettingsShadeTransition(durationScale)
+ toQuickSettingsShadeTransition(edge, durationScale)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToNotificationsShadeTransition.kt
index 372e4a1a..02664c1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToNotificationsShadeTransition.kt
@@ -21,5 +21,5 @@
fun TransitionBuilder.lockscreenToNotificationsShadeTransition(
durationScale: Double = 1.0,
) {
- toNotificationsShadeTransition(durationScale)
+ toNotificationsShadeTransition(durationScale = durationScale)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsShadeTransition.kt
index ce24f5e..19aa3a7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToQuickSettingsShadeTransition.kt
@@ -16,10 +16,11 @@
package com.android.systemui.scene.ui.composable.transitions
+import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
fun TransitionBuilder.lockscreenToQuickSettingsShadeTransition(
durationScale: Double = 1.0,
) {
- toQuickSettingsShadeTransition(durationScale)
+ toQuickSettingsShadeTransition(Edge.Top, durationScale)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 6b3b760..05949b2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -32,6 +32,11 @@
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toNotificationsShadeTransition(
+ /**
+ * The edge where the shade will animate from. This is statically determined (i.e. doesn't
+ * change during runtime).
+ */
+ edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
@@ -50,7 +55,7 @@
}
}
- translate(OverlayShade.Elements.Panel, Edge.Top)
+ translate(OverlayShade.Elements.Panel, edge)
fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
index ec2f14f..9d13647 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToQuickSettingsShadeTransition.kt
@@ -19,17 +19,15 @@
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
import com.android.systemui.shade.ui.composable.OverlayShade
import com.android.systemui.shade.ui.composable.Shade
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.toQuickSettingsShadeTransition(
+ edge: Edge = Edge.Top,
durationScale: Double = 1.0,
) {
spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
@@ -38,17 +36,9 @@
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
)
- distance =
- object : UserActionDistance {
- override fun UserActionDistanceScope.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- return fromSceneSize.height.toFloat() * 2 / 3f
- }
- }
+ distance = UserActionDistance { fromSceneSize, _ -> fromSceneSize.height.toFloat() * 2 / 3f }
- translate(OverlayShade.Elements.Panel, Edge.Top)
+ translate(OverlayShade.Elements.Panel, edge)
fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index c189d73..a94ebc8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -54,6 +54,7 @@
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import com.android.systemui.util.kotlin.getOrNull
import dagger.Lazy
@@ -63,7 +64,6 @@
@Composable
fun SceneScope.OverlayShade(
viewModel: OverlayShadeViewModel,
- panelAlignment: Alignment,
lockscreenContent: Lazy<Optional<LockscreenContent>>,
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
@@ -82,7 +82,12 @@
Box(
modifier = Modifier.fillMaxSize().panelPadding(),
- contentAlignment = panelAlignment,
+ contentAlignment =
+ if (viewModel.panelAlignment == ShadeAlignment.Top) {
+ Alignment.TopEnd
+ } else {
+ Alignment.BottomEnd
+ },
) {
Panel(
modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index edef5fb..4a6599a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -36,7 +36,6 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -97,6 +96,7 @@
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
@@ -138,6 +138,7 @@
private val shadeSession: SaveableSession,
private val notificationStackScrollView: Lazy<NotificationScrollView>,
private val viewModel: ShadeSceneViewModel,
+ private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
private val statusBarIconController: StatusBarIconController,
@@ -157,6 +158,7 @@
ShadeScene(
notificationStackScrollView.get(),
viewModel = viewModel,
+ notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
createTintedIconManager = tintedIconManagerFactory::create,
createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
statusBarIconController = statusBarIconController,
@@ -177,6 +179,7 @@
private fun SceneScope.ShadeScene(
notificationStackScrollView: NotificationScrollView,
viewModel: ShadeSceneViewModel,
+ notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
statusBarIconController: StatusBarIconController,
@@ -191,6 +194,7 @@
SingleShade(
notificationStackScrollView = notificationStackScrollView,
viewModel = viewModel,
+ notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
createTintedIconManager = createTintedIconManager,
createBatteryMeterViewController = createBatteryMeterViewController,
statusBarIconController = statusBarIconController,
@@ -203,6 +207,7 @@
SplitShade(
notificationStackScrollView = notificationStackScrollView,
viewModel = viewModel,
+ notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
createTintedIconManager = createTintedIconManager,
createBatteryMeterViewController = createBatteryMeterViewController,
statusBarIconController = statusBarIconController,
@@ -219,6 +224,7 @@
private fun SceneScope.SingleShade(
notificationStackScrollView: NotificationScrollView,
viewModel: ShadeSceneViewModel,
+ notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
statusBarIconController: StatusBarIconController,
@@ -330,7 +336,7 @@
NotificationScrollingStack(
shadeSession = shadeSession,
stackScrollView = notificationStackScrollView,
- viewModel = viewModel.notifications,
+ viewModel = notificationsPlaceholderViewModel,
maxScrimTop = { maxNotifScrimTop.value },
shadeMode = ShadeMode.Single,
shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
@@ -354,7 +360,7 @@
}
NotificationStackCutoffGuideline(
stackScrollView = notificationStackScrollView,
- viewModel = viewModel.notifications,
+ viewModel = notificationsPlaceholderViewModel,
modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding()
)
}
@@ -364,6 +370,7 @@
private fun SceneScope.SplitShade(
notificationStackScrollView: NotificationScrollView,
viewModel: ShadeSceneViewModel,
+ notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
statusBarIconController: StatusBarIconController,
@@ -431,8 +438,10 @@
label = "alphaAnimationBrightnessMirrorContentHiding",
)
- viewModel.notifications.setAlphaForBrightnessMirror(contentAlpha)
- DisposableEffect(Unit) { onDispose { viewModel.notifications.setAlphaForBrightnessMirror(1f) } }
+ notificationsPlaceholderViewModel.setAlphaForBrightnessMirror(contentAlpha)
+ DisposableEffect(Unit) {
+ onDispose { notificationsPlaceholderViewModel.setAlphaForBrightnessMirror(1f) }
+ }
val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
@@ -474,9 +483,9 @@
BrightnessMirror(
viewModel = viewModel.brightnessMirrorViewModel,
qsSceneAdapter = viewModel.qsSceneAdapter,
- // Need to remove the offset of the header height, as the mirror uses
- // the position of the Brightness slider in the window
- modifier = Modifier.offset(y = -ShadeHeader.Dimensions.CollapsedHeight)
+ // Need to use the offset measured from the container as the header
+ // has to be accounted for
+ measureFromContainer = true
)
Column(
verticalArrangement = Arrangement.Top,
@@ -533,7 +542,7 @@
NotificationScrollingStack(
shadeSession = shadeSession,
stackScrollView = notificationStackScrollView,
- viewModel = viewModel.notifications,
+ viewModel = notificationsPlaceholderViewModel,
maxScrimTop = { 0f },
shouldPunchHoleBehindScrim = false,
shouldReserveSpaceForNavBar = false,
@@ -548,7 +557,7 @@
}
NotificationStackCutoffGuideline(
stackScrollView = notificationStackScrollView,
- viewModel = viewModel.notifications,
+ viewModel = notificationsPlaceholderViewModel,
modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding()
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index c2dd803..ea740a8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -48,8 +48,15 @@
}
return when (transitionState) {
- is TransitionState.Idle ->
- animate(layoutState, target, transitionKey, isInitiatedByUserInput = false)
+ is TransitionState.Idle -> {
+ animate(
+ layoutState,
+ target,
+ transitionKey,
+ isInitiatedByUserInput = false,
+ replacedTransition = null,
+ )
+ }
is TransitionState.Transition -> {
val isInitiatedByUserInput = transitionState.isInitiatedByUserInput
@@ -79,6 +86,7 @@
isInitiatedByUserInput,
initialProgress = progress,
initialVelocity = transitionState.progressVelocity,
+ replacedTransition = transitionState,
)
}
} else if (transitionState.fromScene == target) {
@@ -101,6 +109,7 @@
initialProgress = progress,
initialVelocity = transitionState.progressVelocity,
reversed = true,
+ replacedTransition = transitionState,
)
}
} else {
@@ -137,6 +146,7 @@
isInitiatedByUserInput,
fromScene = animateFrom,
chain = chain,
+ replacedTransition = null,
)
}
}
@@ -148,6 +158,7 @@
targetScene: SceneKey,
transitionKey: TransitionKey?,
isInitiatedByUserInput: Boolean,
+ replacedTransition: TransitionState.Transition?,
initialProgress: Float = 0f,
initialVelocity: Float = 0f,
reversed: Boolean = false,
@@ -164,6 +175,7 @@
currentScene = targetScene,
isInitiatedByUserInput = isInitiatedByUserInput,
isUserInputOngoing = false,
+ replacedTransition = replacedTransition,
)
} else {
OneOffTransition(
@@ -173,6 +185,7 @@
currentScene = targetScene,
isInitiatedByUserInput = isInitiatedByUserInput,
isUserInputOngoing = false,
+ replacedTransition = replacedTransition,
)
}
@@ -214,7 +227,8 @@
override val currentScene: SceneKey,
override val isInitiatedByUserInput: Boolean,
override val isUserInputOngoing: Boolean,
-) : TransitionState.Transition(fromScene, toScene) {
+ replacedTransition: TransitionState.Transition?,
+) : TransitionState.Transition(fromScene, toScene, replacedTransition) {
/**
* The animatable used to animate this transition.
*
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index bb17024..e8fdfc8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -37,7 +37,7 @@
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
-interface DraggableHandler {
+internal interface DraggableHandler {
/**
* Start a drag in the given [startedPosition], with the given [overSlop] and number of
* [pointersDown].
@@ -51,7 +51,7 @@
* The [DragController] provides control over the transition between two scenes through the [onDrag]
* and [onStop] methods.
*/
-interface DragController {
+internal interface DragController {
/** Drag the current scene by [delta] pixels. */
fun onDrag(delta: Float)
@@ -395,10 +395,11 @@
if (
distance != DistanceUnspecified &&
shouldCommitSwipe(
- offset,
- distance,
- velocity,
+ offset = offset,
+ distance = distance,
+ velocity = velocity,
wasCommitted = swipeTransition._currentScene == toScene,
+ requiresFullDistanceSwipe = swipeTransition.requiresFullDistanceSwipe,
)
) {
targetScene = toScene
@@ -472,7 +473,12 @@
distance: Float,
velocity: Float,
wasCommitted: Boolean,
+ requiresFullDistanceSwipe: Boolean,
): Boolean {
+ if (requiresFullDistanceSwipe && !wasCommitted) {
+ return offset / distance >= 1f
+ }
+
fun isCloserToTarget(): Boolean {
return (offset - distance).absoluteValue < offset.absoluteValue
}
@@ -530,6 +536,8 @@
userActionDistanceScope = layoutImpl.userActionDistanceScope,
orientation = orientation,
isUpOrLeft = isUpOrLeft,
+ requiresFullDistanceSwipe = result.requiresFullDistanceSwipe,
+ replacedTransition = null,
)
}
@@ -543,7 +551,10 @@
_toScene = old._toScene,
userActionDistanceScope = old.userActionDistanceScope,
orientation = old.orientation,
- isUpOrLeft = old.isUpOrLeft
+ isUpOrLeft = old.isUpOrLeft,
+ lastDistance = old.lastDistance,
+ requiresFullDistanceSwipe = old.requiresFullDistanceSwipe,
+ replacedTransition = old,
)
.apply {
_currentScene = old._currentScene
@@ -561,8 +572,11 @@
val userActionDistanceScope: UserActionDistanceScope,
override val orientation: Orientation,
override val isUpOrLeft: Boolean,
+ val requiresFullDistanceSwipe: Boolean,
+ replacedTransition: SwipeTransition?,
+ var lastDistance: Float = DistanceUnspecified,
) :
- TransitionState.Transition(_fromScene.key, _toScene.key),
+ TransitionState.Transition(_fromScene.key, _toScene.key, replacedTransition),
TransitionState.HasOverscrollProperties {
var _currentScene by mutableStateOf(_fromScene)
override val currentScene: SceneKey
@@ -620,8 +634,6 @@
get() = distance().absoluteValue
}
- private var lastDistance = DistanceUnspecified
-
/** Whether [TransitionState.Transition.finish] was called on this transition. */
var isFinishing = false
private set
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 09d11b7..377b02b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -306,7 +306,6 @@
return layout(placeable.width, placeable.height) { place(transition, placeable) }
}
- @OptIn(ExperimentalComposeUiApi::class)
private fun Placeable.PlacementScope.place(
transition: TransitionState.Transition?,
placeable: Placeable,
@@ -496,6 +495,10 @@
transition: TransitionState.Transition,
previousTransition: TransitionState.Transition,
) {
+ if (transition.replacedTransition == previousTransition) {
+ return
+ }
+
val sceneStates = element.sceneStates
fun updatedSceneState(key: SceneKey): Element.SceneState? {
return sceneStates[key]?.also { it.selfUpdateValuesBeforeInterruption() }
@@ -561,10 +564,20 @@
}
private fun Element.SceneState.selfUpdateValuesBeforeInterruption() {
- offsetBeforeInterruption = lastOffset
sizeBeforeInterruption = lastSize
- scaleBeforeInterruption = lastScale
- alphaBeforeInterruption = lastAlpha
+
+ if (lastAlpha > 0f) {
+ offsetBeforeInterruption = lastOffset
+ scaleBeforeInterruption = lastScale
+ alphaBeforeInterruption = lastAlpha
+ } else {
+ // Consider the element as not placed in this scene if it was fully transparent.
+ // TODO(b/290930950): Look into using derived state inside place() instead to not even place
+ // the element at all when alpha == 0f.
+ offsetBeforeInterruption = Offset.Unspecified
+ scaleBeforeInterruption = Scale.Unspecified
+ alphaBeforeInterruption = Element.AlphaUnspecified
+ }
}
private fun Element.SceneState.updateValuesBeforeInterruption(lastState: Element.SceneState) {
@@ -1075,8 +1088,8 @@
// range.
val directionSign = if (transition.isUpOrLeft) -1 else 1
val isToScene = overscroll.scene == transition.toScene
- val overscrollProgress = transition.progress.let { if (isToScene) it - 1f else it }
- val progress = directionSign * overscrollProgress
+ val linearProgress = transition.progress.let { if (isToScene) it - 1f else it }
+ val progress = directionSign * overscroll.progressConverter(linearProgress)
val rangeProgress = propertySpec.range?.progress(progress) ?: progress
// Interpolate between the value at rest and the over scrolled value.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 6001f1f..572b1d7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -138,11 +138,26 @@
}
}
+ private var _toFloat = orientation.toFunctionOffsetToFloat()
+
+ private fun Offset.toFloat(): Float = _toFloat(this)
+
+ private fun Orientation.toFunctionOffsetToFloat(): (Offset) -> Float =
+ when (this) {
+ Orientation.Vertical -> {
+ { it.y }
+ }
+ Orientation.Horizontal -> {
+ { it.x }
+ }
+ }
+
var orientation: Orientation = orientation
set(value) {
// Reset the pointer input whenever orientation changed.
if (value != field) {
field = value
+ _toFloat = field.toFunctionOffsetToFloat()
delegate.resetPointerInputHandler()
}
}
@@ -367,13 +382,6 @@
return event
}
- private fun Offset.toFloat(): Float {
- return when (orientation) {
- Orientation.Vertical -> y
- Orientation.Horizontal -> x
- }
- }
-
/**
* Continues to read drag events until all pointers are up or the drag event is canceled. The
* initial pointer to use for driving the drag is [initialPointerId]. [hasDragged] passes the
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index b925130..7c8fce8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -459,9 +459,16 @@
/** The key of the transition that should be used. */
val transitionKey: TransitionKey? = null,
+
+ /**
+ * If `true`, the swipe will be committed and we will settle to [toScene] if only if the user
+ * swiped at least the swipe distance, i.e. the transition progress was already equal to or
+ * bigger than 100% when the user released their finger. `
+ */
+ val requiresFullDistanceSwipe: Boolean = false,
)
-interface UserActionDistance {
+fun interface UserActionDistance {
/**
* Return the **absolute** distance of the user action given the size of the scene we are
* animating from and the [orientation].
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index a8df6f4..5b4fbf0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -224,6 +224,9 @@
/** The scene this transition is going to. Can't be the same as fromScene */
val toScene: SceneKey,
+
+ /** The transition that `this` transition is replacing, if any. */
+ internal val replacedTransition: Transition? = null,
) : TransitionState {
/**
* The key of this transition. This should usually be null, but it can be specified to use a
@@ -279,6 +282,11 @@
init {
check(fromScene != toScene)
+ check(
+ replacedTransition == null ||
+ (replacedTransition.fromScene == fromScene &&
+ replacedTransition.toScene == toScene)
+ )
}
/**
@@ -321,6 +329,10 @@
return 0f
}
+ if (replacedTransition != null) {
+ return replacedTransition.interruptionProgress(layoutImpl)
+ }
+
fun create(): Animatable<Float, AnimationVector1D> {
val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold)
layoutImpl.coroutineScope.launch {
@@ -521,6 +533,10 @@
check(transitionStates.size == 1)
check(transitionStates[0] is TransitionState.Idle)
transitionStates = listOf(transition)
+ } else if (currentState == transition.replacedTransition) {
+ // Replace the transition.
+ transitionStates =
+ transitionStates.subList(0, transitionStates.lastIndex) + transition
} else {
// Append the new transition.
transitionStates = transitionStates + transition
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index 0f6a1d2..e30dd356 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -48,7 +48,8 @@
) {
private val transitionCache =
mutableMapOf<
- SceneKey, MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
+ SceneKey,
+ MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
>()
private val overscrollCache =
@@ -87,8 +88,7 @@
return transition(from, to, key) {
(it.from == to && it.to == null) || (it.to == from && it.from == null)
}
- ?.reversed()
- ?: defaultTransition(from, to)
+ ?.reversed() ?: defaultTransition(from, to)
}
private fun transition(
@@ -257,12 +257,24 @@
/** The [TransformationSpec] associated to this [OverscrollSpec]. */
val transformationSpec: TransformationSpec
+
+ /**
+ * Function that takes a linear overscroll progress value ranging from 0 to +/- infinity and
+ * outputs the desired **overscroll progress value**.
+ *
+ * When the progress value is:
+ * - 0, the user is not overscrolling.
+ * - 1, the user overscrolled by exactly the [OverscrollBuilder.distance].
+ * - Greater than 1, the user overscrolled more than the [OverscrollBuilder.distance].
+ */
+ val progressConverter: (Float) -> Float
}
internal class OverscrollSpecImpl(
override val scene: SceneKey,
override val orientation: Orientation,
override val transformationSpec: TransformationSpecImpl,
+ override val progressConverter: (Float) -> Float,
) : OverscrollSpec
/**
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 465a410..89ed8d6 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
@@ -185,6 +185,17 @@
@TransitionDsl
interface OverscrollBuilder : BaseTransitionBuilder {
+ /**
+ * Function that takes a linear overscroll progress value ranging from 0 to +/- infinity and
+ * outputs the desired **overscroll progress value**.
+ *
+ * When the progress value is:
+ * - 0, the user is not overscrolling.
+ * - 1, the user overscrolled by exactly the [distance].
+ * - Greater than 1, the user overscrolled more than the [distance].
+ */
+ var progressConverter: (Float) -> Float
+
/** Translate the element(s) matching [matcher] by ([x], [y]) pixels. */
fun translate(
matcher: ElementMatcher,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 802ab1f..1e67aa9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -81,16 +81,20 @@
orientation: Orientation,
builder: OverscrollBuilder.() -> Unit
): OverscrollSpec {
- fun transformationSpec(): TransformationSpecImpl {
- val impl = OverscrollBuilderImpl().apply(builder)
- return TransformationSpecImpl(
- progressSpec = snap(),
- swipeSpec = null,
- distance = impl.distance,
- transformations = impl.transformations,
+ val impl = OverscrollBuilderImpl().apply(builder)
+ val spec =
+ OverscrollSpecImpl(
+ scene = scene,
+ orientation = orientation,
+ transformationSpec =
+ TransformationSpecImpl(
+ progressSpec = snap(),
+ swipeSpec = null,
+ distance = impl.distance,
+ transformations = impl.transformations,
+ ),
+ progressConverter = impl.progressConverter
)
- }
- val spec = OverscrollSpecImpl(scene, orientation, transformationSpec())
transitionOverscrollSpecs.add(spec)
return spec
}
@@ -231,6 +235,8 @@
}
internal open class OverscrollBuilderImpl : BaseTransitionBuilderImpl(), OverscrollBuilder {
+ override var progressConverter: (Float) -> Float = { it }
+
override fun translate(
matcher: ElementMatcher,
x: OverscrollScope.() -> Float,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt
new file mode 100644
index 0000000..a4bd2be
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/modifiers/SizeMatcher.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.compose.animation.scene.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.invalidateMeasurement
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.constrain
+
+/**
+ * An object to make one or more destination node be the same size as a source node.
+ *
+ * Important: Most of the time, you should not use this class and instead use `Box` together with
+ * `Modifier.matchParentSize()`. Use this only if you need to use both `Modifier.element()` and
+ * `Modifier.matchParentSize()` (see b/347910697 for details).
+ *
+ * Example:
+ * ```
+ * Box {
+ * val sizeMatcher = remember { SizeMatcher() }
+ *
+ * // The content.
+ * Content(Modifier.sizeMatcherSource(sizeMatcher))
+ *
+ * // The background. Note that this has to be composed after Content() so that it
+ * // is measured after it. We set its zIndex to -1 so that it is still placed
+ * // (drawn) before/below it. We don't use BoxScope.matchParentSize() because it
+ * // does not play well with Modifier.element().
+ * Box(
+ * Modifier.zIndex(-1f)
+ * .element(Background)
+ * // Set the preferred size of this element.
+ * // Important: This must be *after* the Modifier.element() so that
+ * // Modifier.element() can override the size.
+ * .sizeMatcherDestination(sizeMatcher)
+ * .background(
+ * MaterialTheme.colorScheme.primaryContainer,
+ * RoundedCornerShape(32.dp),
+ * )
+ * )
+ * }
+ * ```
+ *
+ * @see sizeMatcherSource
+ * @see sizeMatcherDestination
+ */
+class SizeMatcher {
+ internal var source: LayoutModifierNode? = null
+ set(value) {
+ if (value != null && field != null && value != field) {
+ error("Exactly one Modifier.sizeMatcherSource() should be specified")
+ }
+ }
+
+ internal var destinations = mutableSetOf<LayoutModifierNode>()
+ internal var sourceSize: IntSize = InvalidSize
+ get() {
+ if (field == InvalidSize) {
+ error(
+ "SizeMatcher size was retrieved before it was set. You should make sure that " +
+ "all matcher destination are measured *after* the matcher source."
+ )
+ }
+ return field
+ }
+ set(value) {
+ if (value != field) {
+ field = value
+ destinations.forEach { it.invalidateMeasurement() }
+ }
+ }
+
+ companion object {
+ private val InvalidSize = IntSize(Int.MIN_VALUE, Int.MIN_VALUE)
+ }
+}
+
+/**
+ * Mark this node as the source of a [SizeMatcher].
+ *
+ * Important: There must be only a single source node associated to a [SizeMatcher] and it must be
+ * measured before any destination.
+ */
+fun Modifier.sizeMatcherSource(matcher: SizeMatcher): Modifier {
+ return this.then(SizeMatcherSourceNodeElement(matcher))
+}
+
+/**
+ * Mark this node as the destination of a [SizeMatcher] so that its *preferred* size is the same
+ * size as the source size.
+ *
+ * Important: Destination nodes must be measured *after* the source node, otherwise it might cause
+ * crashes or 1-frame flickers. For most simple layouts (like Box, Row or Column), this usually
+ * means that the destinations nodes must be composed *after* the source node. If doing so is
+ * causing layering issues, you can use `Modifier.zIndex` to explicitly set the placement order of
+ * your composables.
+ */
+fun Modifier.sizeMatcherDestination(matcher: SizeMatcher): Modifier {
+ return this.then(SizeMatcherDestinationElement(matcher))
+}
+
+private data class SizeMatcherSourceNodeElement(
+ private val matcher: SizeMatcher,
+) : ModifierNodeElement<SizeMatcherSourceNode>() {
+ override fun create(): SizeMatcherSourceNode = SizeMatcherSourceNode(matcher)
+
+ override fun update(node: SizeMatcherSourceNode) {
+ node.update(matcher)
+ }
+}
+
+private class SizeMatcherSourceNode(
+ private var matcher: SizeMatcher,
+) : Modifier.Node(), LayoutModifierNode {
+ override fun onAttach() {
+ matcher.source = this
+ }
+
+ override fun onDetach() {
+ matcher.source = null
+ }
+
+ fun update(matcher: SizeMatcher) {
+ val previous = this.matcher
+ this.matcher = matcher
+
+ previous.source = null
+ matcher.source = this
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ return measurable.measure(constraints).run {
+ matcher.sourceSize = IntSize(width, height)
+ layout(width, height) { place(0, 0) }
+ }
+ }
+}
+
+private data class SizeMatcherDestinationElement(
+ private val matcher: SizeMatcher,
+) : ModifierNodeElement<SizeMatcherDestinationNode>() {
+ override fun create(): SizeMatcherDestinationNode = SizeMatcherDestinationNode(matcher)
+
+ override fun update(node: SizeMatcherDestinationNode) {
+ node.update(matcher)
+ }
+}
+
+private class SizeMatcherDestinationNode(
+ private var matcher: SizeMatcher,
+) : Modifier.Node(), LayoutModifierNode {
+ override fun onAttach() {
+ this.matcher.destinations.add(this)
+ }
+
+ override fun onDetach() {
+ this.matcher.destinations.remove(this)
+ }
+
+ fun update(matcher: SizeMatcher) {
+ val previous = this.matcher
+ this.matcher = matcher
+
+ previous.destinations.remove(this)
+ matcher.destinations.add(this)
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val preferredSize = matcher.sourceSize
+ val preferredConstraints = Constraints.fixed(preferredSize.width, preferredSize.height)
+
+ // Make sure we still respect the incoming constraints.
+ val placeable = measurable.measure(constraints.constrain(preferredConstraints))
+ return layout(placeable.width, placeable.height) { placeable.place(0, 0) }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index f532e2e..65b388f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -1195,4 +1195,54 @@
assertThat(transition).hasProgress(0f)
assertThat(transition).hasOverscrollSpec()
}
+
+ @Test
+ fun interceptingTransitionKeepsDistance() = runGestureTest {
+ var swipeDistance = 75f
+ layoutState.transitions = transitions {
+ from(SceneA, to = SceneB) { distance = UserActionDistance { _, _ -> swipeDistance } }
+ }
+
+ // Start transition.
+ val controller = onDragStarted(overSlop = -50f)
+ assertTransition(fromScene = SceneA, toScene = SceneB, progress = 50f / 75f)
+
+ // Intercept the transition and change the swipe distance. The original distance and
+ // progress should be the same.
+ swipeDistance = 50f
+ controller.onDragStopped(0f)
+ onDragStartedImmediately()
+ assertTransition(fromScene = SceneA, toScene = SceneB, progress = 50f / 75f)
+ }
+
+ @Test
+ fun requireFullDistanceSwipe() = runGestureTest {
+ mutableUserActionsA[Swipe.Up] = UserActionResult(SceneB, requiresFullDistanceSwipe = true)
+
+ val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.9f))
+ assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.9f)
+
+ controller.onDragStopped(velocity = 0f)
+ advanceUntilIdle()
+ assertIdle(SceneA)
+
+ val otherController = onDragStarted(overSlop = up(fractionOfScreen = 1f))
+ assertTransition(fromScene = SceneA, toScene = SceneB, progress = 1f)
+ otherController.onDragStopped(velocity = 0f)
+ advanceUntilIdle()
+ assertIdle(SceneB)
+ }
+
+ @Test
+ fun interceptingTransitionReplacesCurrentTransition() = runGestureTest {
+ val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.5f))
+ val transition = assertThat(layoutState.transitionState).isTransition()
+ controller.onDragStopped(velocity = 0f)
+
+ // Intercept the transition.
+ onDragStartedImmediately()
+ val newTransition = assertThat(layoutState.transitionState).isTransition()
+ assertThat(newTransition).isNotSameInstanceAs(transition)
+ assertThat(newTransition.replacedTransition).isSameInstanceAs(transition)
+ }
}
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 a18da73..2de6faa 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
@@ -731,7 +731,7 @@
onAnimatedFloat = { animatedFloat = it },
)
- val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
fooElement.assertTopPositionInRootIsEqualTo(0.dp)
val transition = assertThat(state.transitionState).isTransition()
assertThat(transition).isNotNull()
@@ -811,7 +811,7 @@
}
assertThat(state.transitionState).isIdle()
- val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
fooElement.assertTopPositionInRootIsEqualTo(0.dp)
// Swipe by half of verticalSwipeDistance.
@@ -858,7 +858,7 @@
onAnimatedFloat = { animatedFloat = it },
)
- val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
fooElement.assertTopPositionInRootIsEqualTo(0.dp)
assertThat(animatedFloat).isEqualTo(100f)
@@ -889,6 +889,63 @@
}
@Test
+ fun elementTransitionWithDistanceDuringOverscrollWithProgressConverter() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ var animatedFloat = 0f
+ val state =
+ setupOverscrollScenario(
+ layoutWidth = layoutWidth,
+ layoutHeight = layoutHeight,
+ sceneTransitions = {
+ overscroll(SceneB, Orientation.Vertical) {
+ // Overscroll progress will be halved
+ progressConverter = { it / 2f }
+
+ // On overscroll 100% -> Foo should translate by layoutHeight
+ translate(TestElements.Foo, y = { absoluteDistance })
+ }
+ },
+ firstScroll = 1f, // 100% scroll
+ animatedFloatRange = 0f..100f,
+ onAnimatedFloat = { animatedFloat = it },
+ )
+
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+ assertThat(animatedFloat).isEqualTo(100f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ val transition = assertThat(state.transitionState).isTransition()
+ assertThat(animatedFloat).isEqualTo(100f)
+
+ // Scroll 200% (100% scroll + 100% overscroll)
+ assertThat(transition).hasProgress(2f)
+ assertThat(transition).hasOverscrollSpec()
+
+ // Overscroll progress is halved, we are at 50% of the overscroll progress.
+ fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f)
+ assertThat(animatedFloat).isEqualTo(100f)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 100%
+ moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+ }
+
+ // Scroll 300% (100% scroll + 200% overscroll)
+ assertThat(transition).hasProgress(3f)
+ assertThat(transition).hasOverscrollSpec()
+
+ // Overscroll progress is halved, we are at 100% of the overscroll progress.
+ fooElement.assertTopPositionInRootIsEqualTo(layoutHeight)
+ assertThat(animatedFloat).isEqualTo(100f)
+ }
+
+ @Test
fun elementTransitionWithDistanceDuringOverscrollBouncing() {
val layoutWidth = 200.dp
val layoutHeight = 400.dp
@@ -914,7 +971,7 @@
onAnimatedFloat = { animatedFloat = it },
)
- val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
fooElement.assertTopPositionInRootIsEqualTo(0.dp)
assertThat(animatedFloat).isEqualTo(100f)
@@ -1937,4 +1994,119 @@
)
.isEqualTo(Element.SizeUnspecified)
}
+
+ @Test
+ fun transparentElementIsNotImpactingInterruption() = runTest {
+ val state =
+ rule.runOnIdle {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions {
+ from(SceneA, to = SceneB) {
+ // In A => B, Foo is not shared and first fades out from A then fades in
+ // B.
+ sharedElement(TestElements.Foo, enabled = false)
+ fractionRange(end = 0.5f) { fade(TestElements.Foo.inScene(SceneA)) }
+ fractionRange(start = 0.5f) { fade(TestElements.Foo.inScene(SceneB)) }
+ }
+
+ from(SceneB, to = SceneA) {
+ // In B => A, Foo is shared.
+ sharedElement(TestElements.Foo, enabled = true)
+ }
+ }
+ )
+ }
+
+ @Composable
+ fun SceneScope.Foo(modifier: Modifier = Modifier) {
+ Box(modifier.element(TestElements.Foo).size(10.dp))
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state) {
+ scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+
+ // Define A after B so that Foo is placed in A during A <=> B.
+ scene(SceneA) { Foo() }
+ }
+ }
+
+ // Start A => B at 70%.
+ rule.runOnUiThread {
+ state.startTransition(
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.7f },
+ onFinish = neverFinish(),
+ )
+ )
+ }
+
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(40.dp, 60.dp)
+
+ // Start B => A at 50% with interruptionProgress = 100%. Foo is placed in A and should still
+ // be at (40dp, 60dp) given that it was fully transparent in A before the interruption.
+ var interruptionProgress by mutableStateOf(1f)
+ rule.runOnUiThread {
+ state.startTransition(
+ transition(
+ from = SceneB,
+ to = SceneA,
+ progress = { 0.5f },
+ interruptionProgress = { interruptionProgress },
+ onFinish = neverFinish(),
+ )
+ )
+ }
+
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertPositionInRootIsEqualTo(40.dp, 60.dp)
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()
+
+ // Set the interruption progress to 0%. Foo should be at (20dp, 30dp) given that B => is at
+ // 50%.
+ interruptionProgress = 0f
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()
+ }
+
+ @Test
+ fun replacedTransitionDoesNotTriggerInterruption() = runTest {
+ val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
+
+ @Composable
+ fun SceneScope.Foo(modifier: Modifier = Modifier) {
+ Box(modifier.element(TestElements.Foo).size(10.dp))
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { Foo() }
+ scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+ }
+ }
+
+ // Start A => B at 50%.
+ val aToB1 =
+ transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
+ rule.runOnUiThread { state.startTransition(aToB1) }
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
+
+ // Replace A => B by another A => B at 100%. Even with interruption progress at 100%, Foo
+ // should be at (40dp, 60dp) given that aToB1 was replaced by aToB2.
+ val aToB2 =
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 1f },
+ interruptionProgress = { 1f },
+ replacedTransition = aToB1,
+ )
+ rule.runOnUiThread { state.startTransition(aToB2) }
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(40.dp, 60.dp)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index 09d1a82..3552d3d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -131,10 +131,6 @@
assertThat(state.currentTransitions)
.comparingElementsUsing(FromToCurrentTriple)
.containsExactly(
- // Initial transition A to B. This transition will never be consumed by anyone given
- // that it has the same (from, to) pair as the next transition.
- Triple(SceneA, SceneB, SceneB),
-
// Initial transition reversed, B back to A.
Triple(SceneA, SceneB, SceneA),
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
index 322b035..65f4f9e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
@@ -37,8 +37,11 @@
bouncingScene: SceneKey? = null,
orientation: Orientation = Orientation.Horizontal,
onFinish: ((TransitionState.Transition) -> Job)? = null,
+ replacedTransition: TransitionState.Transition? = null,
): TransitionState.Transition {
- return object : TransitionState.Transition(from, to), TransitionState.HasOverscrollProperties {
+ return object :
+ TransitionState.Transition(from, to, replacedTransition),
+ TransitionState.HasOverscrollProperties {
override val currentScene: SceneKey
get() = current()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeMatcherTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeMatcherTest.kt
new file mode 100644
index 0000000..fa24966
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/modifiers/SizeMatcherTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene.modifiers
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.assertSizeIsEqualTo
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SizeMatcherTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun sizeMatcher() {
+ val contentSize = DpSize(200.dp, 100.dp)
+ val sizeMatcher = SizeMatcher()
+ val backgroundTag = "background"
+
+ rule.setContent {
+ Box {
+ Box(Modifier.sizeMatcherSource(sizeMatcher).size(contentSize))
+ Box(Modifier.testTag(backgroundTag).sizeMatcherDestination(sizeMatcher))
+ }
+ }
+
+ rule.onNodeWithTag(backgroundTag).assertSizeIsEqualTo(contentSize.width, contentSize.height)
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
index e39d7ed..9e857deb 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
@@ -21,16 +21,16 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/** Provides access to state related to notification settings. */
class NotificationSettingsRepository(
- scope: CoroutineScope,
+ private val scope: CoroutineScope,
private val backgroundDispatcher: CoroutineDispatcher,
private val secureSettingsRepository: SecureSettingsRepository,
) {
@@ -41,16 +41,15 @@
.distinctUntilChanged()
/** The current state of the notification setting. */
- val isShowNotificationsOnLockScreenEnabled: StateFlow<Boolean> =
+ suspend fun isShowNotificationsOnLockScreenEnabled(): StateFlow<Boolean> =
secureSettingsRepository
.intSetting(
name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
)
.map { it == 1 }
+ .flowOn(backgroundDispatcher)
.stateIn(
scope = scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
)
suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
index 04e8090..b4105bd 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/domain/interactor/NotificationSettingsInteractor.kt
@@ -26,8 +26,8 @@
val isNotificationHistoryEnabled = repository.isNotificationHistoryEnabled
/** Should notifications be visible on the lockscreen? */
- val isShowNotificationsOnLockScreenEnabled: StateFlow<Boolean> =
- repository.isShowNotificationsOnLockScreenEnabled
+ suspend fun isShowNotificationsOnLockScreenEnabled(): StateFlow<Boolean> =
+ repository.isShowNotificationsOnLockScreenEnabled()
suspend fun setShowNotificationsOnLockscreenEnabled(enabled: Boolean) {
repository.setShowNotificationsOnLockscreenEnabled(enabled)
@@ -35,7 +35,7 @@
/** Toggles the setting to show or hide notifications on the lock screen. */
suspend fun toggleShowNotificationsOnLockscreenEnabled() {
- val current = repository.isShowNotificationsOnLockScreenEnabled.value
+ val current = repository.isShowNotificationsOnLockScreenEnabled().value
repository.setShowNotificationsOnLockscreenEnabled(!current)
}
}
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index 4def93f..2fd7f1b 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -27088,17 +27088,6 @@
<issue
id="UselessParent"
- message="This `FrameLayout` layout or its `LinearLayout` parent is unnecessary"
- errorLine1=" <FrameLayout"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/res/layout/media_output_list_item.xml"
- line="24"
- column="6"/>
- </issue>
-
- <issue
- id="UselessParent"
message="This `LinearLayout` layout or its `FrameLayout` parent is possibly unnecessary; transfer the `background` attribute to the other view"
errorLine1=" <LinearLayout"
errorLine2=" ~~~~~~~~~~~~">
@@ -30587,17 +30576,6 @@
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
- errorLine1=" <ImageView"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/packages/SystemUI/res/layout/media_output_list_item.xml"
- line="54"
- column="14"/>
- </issue>
-
- <issue
- id="ContentDescription"
- message="Missing `contentDescription` attribute on image"
errorLine1=" <ImageView"
errorLine2=" ~~~~~~~~~">
<location
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 60b48f2..242e822 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -324,7 +324,6 @@
fun showUdfpsOverlay_awake() =
testScope.runTest {
withReason(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
powerRepository.updateWakefulness(
rawState = WakefulnessState.AWAKE,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -341,7 +340,6 @@
fun showUdfpsOverlay_whileGoingToSleep() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OFF,
to = KeyguardState.GONE,
@@ -370,7 +368,6 @@
fun showUdfpsOverlay_whileAsleep() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OFF,
to = KeyguardState.GONE,
@@ -399,7 +396,6 @@
fun neverRemoveViewThatHasNotBeenAdded() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
controllerOverlay.show(udfpsController, overlayParams)
val view = controllerOverlay.getTouchOverlay()
view?.let {
@@ -414,7 +410,6 @@
fun showUdfpsOverlay_afterFinishedTransitioningToAOD() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OFF,
to = KeyguardState.GONE,
@@ -542,7 +537,6 @@
fun addViewPending_layoutIsNotUpdated() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
// GIVEN going to sleep
@@ -580,7 +574,6 @@
fun updateOverlayParams_viewLayoutUpdated() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
powerRepository.updateWakefulness(
rawState = WakefulnessState.AWAKE,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 3035481..68cfa28 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -29,7 +29,6 @@
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
@@ -223,23 +222,14 @@
}
private fun givenAlternateBouncerSupported() {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- kosmos.fingerprintPropertyRepository.supportsUdfps()
- } else {
- kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true)
- }
+ kosmos.givenAlternateBouncerSupported()
}
private fun givenCanShowAlternateBouncer() {
- givenAlternateBouncerSupported()
- kosmos.keyguardBouncerRepository.setPrimaryShow(false)
- kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
- kosmos.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
- whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
- whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
+ kosmos.givenCanShowAlternateBouncer()
}
private fun givenCannotShowAlternateBouncer() {
- kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ kosmos.givenCannotShowAlternateBouncer()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index a5acf72..ccddc9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -38,7 +38,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.sceneContainerStartable
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index cf14547..fbe2c2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -451,6 +451,24 @@
}
}
+ @Test
+ fun transitionFromDozingToGlanceableHub_forcesCommunal() =
+ with(kosmos) {
+ testScope.runTest {
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ communalSceneInteractor.changeScene(CommunalScenes.Blank)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ testScope = this
+ )
+
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ }
+ }
+
private fun TestScope.updateDocked(docked: Boolean) =
with(kosmos) {
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
index 5e120b5..a8bdc7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
@@ -18,8 +18,8 @@
import android.content.Context
import android.content.Intent
-import android.content.SharedPreferences
import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_MAIN
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -30,108 +30,87 @@
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.fakeUserFileManager
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.FakeSharedPreferences
import com.google.common.truth.Truth.assertThat
-import java.io.File
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
- @Mock private lateinit var tableLogBuffer: TableLogBuffer
-
- private lateinit var underTest: CommunalPrefsRepositoryImpl
-
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private lateinit var userRepository: FakeUserRepository
- private lateinit var userFileManager: UserFileManager
+ private val userFileManager: UserFileManager = spy(kosmos.fakeUserFileManager)
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- userRepository = kosmos.fakeUserRepository
- userRepository.setUserInfos(USER_INFOS)
-
- userFileManager =
- FakeUserFileManager(
- mapOf(
- USER_INFOS[0].id to FakeSharedPreferences(),
- USER_INFOS[1].id to FakeSharedPreferences()
- )
- )
+ private val underTest: CommunalPrefsRepositoryImpl by lazy {
+ CommunalPrefsRepositoryImpl(
+ kosmos.testDispatcher,
+ userFileManager,
+ kosmos.broadcastDispatcher,
+ logcatLogBuffer("CommunalPrefsRepositoryImplTest"),
+ )
}
@Test
fun isCtaDismissedValue_byDefault_isFalse() =
testScope.runTest {
- underTest = createCommunalPrefsRepositoryImpl(userFileManager)
- val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
assertThat(isCtaDismissed).isFalse()
}
@Test
fun isCtaDismissedValue_onSet_isTrue() =
testScope.runTest {
- underTest = createCommunalPrefsRepositoryImpl(userFileManager)
- val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
- underTest.setCtaDismissedForCurrentUser()
+ underTest.setCtaDismissed(MAIN_USER)
assertThat(isCtaDismissed).isTrue()
}
@Test
- fun isCtaDismissedValue_whenSwitchUser() =
+ fun isCtaDismissedValue_onSetForDifferentUser_isStillFalse() =
testScope.runTest {
- underTest = createCommunalPrefsRepositoryImpl(userFileManager)
- val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
- underTest.setCtaDismissedForCurrentUser()
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
- // dismissed true for primary user
- assertThat(isCtaDismissed).isTrue()
-
- // switch to secondary user
- userRepository.setSelectedUserInfo(USER_INFOS[1])
-
- // dismissed is false for secondary user
+ underTest.setCtaDismissed(SECONDARY_USER)
assertThat(isCtaDismissed).isFalse()
+ }
- // switch back to primary user
- userRepository.setSelectedUserInfo(USER_INFOS[0])
+ @Test
+ fun isDisclaimerDismissed_byDefault_isFalse() =
+ testScope.runTest {
+ val isDisclaimerDismissed by
+ collectLastValue(underTest.isDisclaimerDismissed(MAIN_USER))
+ assertThat(isDisclaimerDismissed).isFalse()
+ }
- // dismissed is true for primary user
- assertThat(isCtaDismissed).isTrue()
+ @Test
+ fun isDisclaimerDismissed_onSet_isTrue() =
+ testScope.runTest {
+ val isDisclaimerDismissed by
+ collectLastValue(underTest.isDisclaimerDismissed(MAIN_USER))
+
+ underTest.setDisclaimerDismissed(MAIN_USER)
+ assertThat(isDisclaimerDismissed).isTrue()
}
@Test
fun getSharedPreferences_whenFileRestored() =
testScope.runTest {
- val userFileManagerSpy = Mockito.spy(userFileManager)
- underTest = createCommunalPrefsRepositoryImpl(userFileManagerSpy)
-
- val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
- userRepository.setSelectedUserInfo(USER_INFOS[0])
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
assertThat(isCtaDismissed).isFalse()
- clearInvocations(userFileManagerSpy)
+ clearInvocations(userFileManager)
// Received restore finished event.
kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly(
@@ -141,48 +120,12 @@
runCurrent()
// Get shared preferences from the restored file.
- verify(userFileManagerSpy, atLeastOnce())
- .getSharedPreferences(
- FILE_NAME,
- Context.MODE_PRIVATE,
- userRepository.getSelectedUserInfo().id
- )
+ verify(userFileManager, atLeastOnce())
+ .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, MAIN_USER.id)
}
- private fun createCommunalPrefsRepositoryImpl(userFileManager: UserFileManager) =
- CommunalPrefsRepositoryImpl(
- testScope.backgroundScope,
- kosmos.testDispatcher,
- userRepository,
- userFileManager,
- kosmos.broadcastDispatcher,
- logcatLogBuffer("CommunalPrefsRepositoryImplTest"),
- tableLogBuffer,
- )
-
- private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
- UserFileManager {
- override fun getFile(fileName: String, userId: Int): File {
- throw UnsupportedOperationException()
- }
-
- override fun getSharedPreferences(
- fileName: String,
- mode: Int,
- userId: Int
- ): SharedPreferences {
- if (fileName != FILE_NAME) {
- throw IllegalArgumentException("Preference files must be $FILE_NAME")
- }
- return sharedPrefs.getValue(userId)
- }
- }
-
companion object {
- val USER_INFOS =
- listOf(
- UserInfo(/* id= */ 0, "zero", /* flags= */ 0),
- UserInfo(/* id= */ 1, "secondary", /* flags= */ 0),
- )
+ val MAIN_USER = UserInfo(0, "main", FLAG_MAIN)
+ val SECONDARY_USER = UserInfo(1, "secondary", 0)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 3d454a2..d951cca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -488,8 +488,16 @@
@Test
fun ctaTile_afterDismiss_doesNotShow() =
testScope.runTest {
+ // Set to main user, so we can dismiss the tile for the main user.
+ val user = userRepository.asMainUser()
+ userTracker.set(
+ userInfos = listOf(user),
+ selectedUserIndex = 0,
+ )
+ runCurrent()
+
tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalPrefsRepository.setCtaDismissedForCurrentUser()
+ communalPrefsRepository.setCtaDismissed(user)
val ctaTileContent by collectLastValue(underTest.ctaTileContent)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
new file mode 100644
index 0000000..7b79d28
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_MAIN
+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.settings.fakeUserTracker
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalPrefsInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val underTest by lazy { kosmos.communalPrefsInteractor }
+
+ @Test
+ fun setCtaDismissed_currentUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+
+ assertThat(isCtaDismissed).isFalse()
+ underTest.setCtaDismissed(MAIN_USER)
+ assertThat(isCtaDismissed).isTrue()
+ }
+
+ @Test
+ fun setCtaDismissed_anotherUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+
+ assertThat(isCtaDismissed).isFalse()
+ underTest.setCtaDismissed(SECONDARY_USER)
+ assertThat(isCtaDismissed).isFalse()
+ }
+
+ @Test
+ fun isCtaDismissed_userSwitch() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ underTest.setCtaDismissed(MAIN_USER)
+ val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+
+ assertThat(isCtaDismissed).isTrue()
+ setSelectedUser(SECONDARY_USER)
+ assertThat(isCtaDismissed).isFalse()
+ }
+
+ @Test
+ fun setDisclaimerDismissed_currentUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
+
+ assertThat(isDisclaimerDismissed).isFalse()
+ underTest.setDisclaimerDismissed(MAIN_USER)
+ assertThat(isDisclaimerDismissed).isTrue()
+ }
+
+ @Test
+ fun setDisclaimerDismissed_anotherUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
+
+ assertThat(isDisclaimerDismissed).isFalse()
+ underTest.setDisclaimerDismissed(SECONDARY_USER)
+ assertThat(isDisclaimerDismissed).isFalse()
+ }
+
+ @Test
+ fun isDisclaimerDismissed_userSwitch() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ underTest.setDisclaimerDismissed(MAIN_USER)
+ val isDisclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
+
+ assertThat(isDisclaimerDismissed).isTrue()
+ setSelectedUser(SECONDARY_USER)
+ assertThat(isDisclaimerDismissed).isFalse()
+ }
+
+ private suspend fun setSelectedUser(user: UserInfo) {
+ with(kosmos.fakeUserRepository) {
+ setUserInfos(listOf(user))
+ setSelectedUserInfo(user)
+ }
+ kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
+ }
+
+ private companion object {
+ val MAIN_USER = UserInfo(0, "main", FLAG_MAIN)
+ val SECONDARY_USER = UserInfo(1, "secondary", 0)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index 6e48b99..43293c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -83,24 +83,27 @@
}
@Test
- fun snapToSceneForActivity() =
+ fun changeSceneForActivityStartOnDismissKeyguard() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
-
- underTest.snapToSceneForActivityStart(CommunalScenes.Communal)
+ underTest.snapToScene(CommunalScenes.Communal)
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+
+ underTest.changeSceneForActivityStartOnDismissKeyguard()
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
}
@Test
- fun snapToSceneForActivity_willNotChangeScene_forEditModeActivity() =
+ fun changeSceneForActivityStartOnDismissKeyguard_willNotChangeScene_forEditModeActivity() =
testScope.runTest {
val currentScene by collectLastValue(underTest.currentScene)
- assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+ underTest.snapToScene(CommunalScenes.Communal)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
underTest.setEditModeState(EditModeState.STARTING)
- underTest.snapToSceneForActivityStart(CommunalScenes.Communal)
- assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+ underTest.changeSceneForActivityStartOnDismissKeyguard()
+ assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index d5fe2a1..0190ccb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -48,6 +49,8 @@
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -57,6 +60,7 @@
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -104,10 +108,12 @@
smartspaceRepository = kosmos.fakeSmartspaceRepository
mediaRepository = kosmos.fakeCommunalMediaRepository
communalSceneInteractor = kosmos.communalSceneInteractor
+ kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
kosmos.fakeUserTracker.set(
userInfos = listOf(MAIN_USER_INFO),
selectedUserIndex = 0,
)
+ kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
underTest =
@@ -120,6 +126,7 @@
uiEventLogger,
logcatLogBuffer("CommunalEditModeViewModelTest"),
kosmos.testDispatcher,
+ kosmos.communalPrefsInteractor,
)
}
@@ -312,6 +319,29 @@
}
}
+ @Test
+ fun showDisclaimer_trueAfterEditModeShowing() =
+ testScope.runTest {
+ val showDisclaimer by collectLastValue(underTest.showDisclaimer)
+
+ assertThat(showDisclaimer).isFalse()
+ underTest.setEditModeState(EditModeState.SHOWING)
+ assertThat(showDisclaimer).isTrue()
+ }
+
+ @Test
+ fun showDisclaimer_falseWhenDismissed() =
+ testScope.runTest {
+ underTest.setEditModeState(EditModeState.SHOWING)
+ kosmos.fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
+
+ val showDisclaimer by collectLastValue(underTest.showDisclaimer)
+
+ assertThat(showDisclaimer).isTrue()
+ underTest.onDisclaimerDismissed()
+ assertThat(showDisclaimer).isFalse()
+ }
+
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index e7a7b15..51991de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -75,6 +75,7 @@
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
+import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -91,6 +92,7 @@
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -100,7 +102,6 @@
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
@Mock private lateinit var mediaHost: MediaHost
- @Mock private lateinit var user: UserInfo
@Mock private lateinit var providerInfo: AppWidgetProviderInfo
private val kosmos = testKosmos()
@@ -155,13 +156,14 @@
context.resources,
kosmos.keyguardTransitionInteractor,
kosmos.keyguardInteractor,
+ mock<KeyguardIndicationController>(),
kosmos.communalSceneInteractor,
kosmos.communalInteractor,
kosmos.communalSettingsInteractor,
kosmos.communalTutorialInteractor,
kosmos.shadeInteractor,
mediaHost,
- logcatLogBuffer("CommunalViewModelTest"),
+ logcatLogBuffer("CommunalViewModelTest")
)
}
@@ -315,6 +317,7 @@
@Test
fun dismissCta_hidesCtaTileAndShowsPopup_thenHidesPopupAfterTimeout() =
testScope.runTest {
+ setIsMainUser(true)
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
val communalContent by collectLastValue(underTest.communalContent)
@@ -338,6 +341,7 @@
@Test
fun popup_onDismiss_hidesImmediately() =
testScope.runTest {
+ setIsMainUser(true)
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
val currentPopup by collectLastValue(underTest.currentPopup)
@@ -357,7 +361,7 @@
val currentPopup by collectLastValue(underTest.currentPopup)
assertThat(currentPopup).isNull()
- underTest.onShowCustomizeWidgetButton()
+ underTest.onLongClick()
assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton)
advanceTimeBy(POPUP_AUTO_HIDE_TIMEOUT_MS)
assertThat(currentPopup).isNull()
@@ -369,7 +373,7 @@
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
val currentPopup by collectLastValue(underTest.currentPopup)
- underTest.onShowCustomizeWidgetButton()
+ underTest.onLongClick()
assertThat(currentPopup).isEqualTo(PopupType.CustomizeWidgetButton)
underTest.onHidePopup()
@@ -743,13 +747,17 @@
}
private suspend fun setIsMainUser(isMainUser: Boolean) {
- whenever(user.isMain).thenReturn(isMainUser)
- userRepository.setUserInfos(listOf(user))
- userRepository.setSelectedUserInfo(user)
+ val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO
+ with(userRepository) {
+ setUserInfos(listOf(user))
+ setSelectedUserInfo(user)
+ }
+ kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
}
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ val SECONDARY_USER_INFO = UserInfo(1, "secondary", 0)
@JvmStatic
@Parameters(name = "{0}")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryTest.kt
new file mode 100644
index 0000000..79115ae
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryTest.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.deviceconfig.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.fakeDeviceConfigProxy
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceConfigRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val dataSource = kosmos.fakeDeviceConfigProxy
+
+ private val underTest = kosmos.deviceConfigRepository
+
+ @Test
+ fun booleanProperty() =
+ testScope.runTest {
+ val property by collectLastValue(underTest.property("namespace", "name", false))
+ assertThat(property).isFalse()
+
+ dataSource.setProperty("namespace", "name", "true", /* makeDefault= */ false)
+ kosmos.fakeExecutor.runAllReady()
+ assertThat(property).isTrue()
+
+ dataSource.setProperty("namespace", "name", "false", /* makeDefault= */ false)
+ kosmos.fakeExecutor.runAllReady()
+ assertThat(property).isFalse()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorTest.kt
new file mode 100644
index 0000000..6ec4cea
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.deviceconfig.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.fakeDeviceConfigProxy
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceConfigInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val dataSource = kosmos.fakeDeviceConfigProxy
+
+ private val underTest = kosmos.deviceConfigInteractor
+
+ @Test
+ fun booleanProperty() =
+ testScope.runTest {
+ val property by collectLastValue(underTest.property("namespace", "name", false))
+ assertThat(property).isFalse()
+
+ dataSource.setProperty("namespace", "name", "true", /* makeDefault= */ false)
+ kosmos.fakeExecutor.runAllReady()
+ assertThat(property).isTrue()
+
+ dataSource.setProperty("namespace", "name", "false", /* makeDefault= */ false)
+ kosmos.fakeExecutor.runAllReady()
+ assertThat(property).isFalse()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index ee8a22c..5a39de8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -778,6 +778,7 @@
DREAM_COMPONENT,
false /*shouldShowComplication*/
)
+ testScope.runCurrent()
mMainExecutor.runAllReady()
assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index 74eee9b..7d0f040 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -274,6 +274,29 @@
assertThat(couldClick).isFalse()
}
+ @Test
+ fun onTileClick_whileIdle_withQSTile_clicks() =
+ testWhileInState(QSLongPressEffect.State.IDLE) {
+ // GIVEN that a click was detected
+ val couldClick = longPressEffect.onTileClick()
+
+ // THEN the click is successful
+ assertThat(couldClick).isTrue()
+ }
+
+ @Test
+ fun onTileClick_whileIdle_withoutQSTile_cannotClick() =
+ testWhileInState(QSLongPressEffect.State.IDLE) {
+ // GIVEN that no QSTile has been set
+ longPressEffect.qsTile = null
+
+ // GIVEN that a click was detected
+ val couldClick = longPressEffect.onTileClick()
+
+ // THEN the click is not successful
+ assertThat(couldClick).isFalse()
+ }
+
private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 4587ea6..c5ba02d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -528,6 +528,30 @@
}
@Test
+ fun userChange_isFingerprintEnrolledAndEnabledUpdated() =
+ testScope.runTest {
+ createBiometricSettingsRepository()
+ whenever(authController.isFingerprintEnrolled(ANOTHER_USER_ID)).thenReturn(false)
+ whenever(authController.isFingerprintEnrolled(PRIMARY_USER_ID)).thenReturn(true)
+
+ verify(biometricManager)
+ .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
+ val isFingerprintEnrolledAndEnabled =
+ collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
+ biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID)
+ runCurrent()
+ userRepository.setSelectedUserInfo(ANOTHER_USER)
+ runCurrent()
+ assertThat(isFingerprintEnrolledAndEnabled()).isFalse()
+
+ biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID)
+ runCurrent()
+ userRepository.setSelectedUserInfo(PRIMARY_USER)
+ runCurrent()
+ assertThat(isFingerprintEnrolledAndEnabled()).isTrue()
+ }
+
+ @Test
fun userChange_biometricEnabledChange_handlesRaceCondition() =
testScope.runTest {
createBiometricSettingsRepository()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 48caf3e..bd6cfff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -20,6 +20,7 @@
import android.hardware.biometrics.BiometricSourceType
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
@@ -73,6 +74,7 @@
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
@Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
@Captor private lateinit var updateCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
private val mainDispatcher = StandardTestDispatcher()
private val testDispatcher = StandardTestDispatcher()
@@ -100,6 +102,7 @@
systemClock,
facePropertyRepository,
userTracker,
+ lockPatternUtils,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
index 5f0f24d..2d12150 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -61,7 +62,8 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testScope = TestScope()
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
userRepository = FakeUserRepository()
userRepository.setUserInfos(users)
val logger =
@@ -69,7 +71,13 @@
LogBuffer("TestBuffer", 1, mock(LogcatEchoTracker::class.java), false)
)
underTest =
- TrustRepositoryImpl(testScope.backgroundScope, userRepository, trustManager, logger)
+ TrustRepositoryImpl(
+ testScope.backgroundScope,
+ testDispatcher,
+ userRepository,
+ trustManager,
+ logger,
+ )
}
fun TestScope.init() {
@@ -275,4 +283,11 @@
userRepository.setSelectedUserInfo(users[1])
assertThat(trustUsuallyManaged).isFalse()
}
+
+ @Test
+ fun reportKeyguardShowingChanged() =
+ testScope.runTest {
+ underTest.reportKeyguardShowingChanged()
+ verify(trustManager).reportKeyguardShowingChanged()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index d20fec4..5115f5a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -90,7 +90,11 @@
)
reset(transitionRepository)
+ kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+ assertThat(transitionRepository).noTransitionsStarted()
+
kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
runCurrent()
kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 612f2e7..0792a50 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -34,12 +34,14 @@
import android.os.PowerManager
import android.platform.test.annotations.EnableFlags
+import android.service.dream.dreamManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -64,8 +66,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -120,6 +124,66 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToLockscreen_onWakeup_canDream_glanceableHubAvailable() =
+ testScope.runTest {
+ whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // If dreaming is possible and communal is available, then we should transition to
+ // GLANCEABLE_HUB when waking up.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToLockscreen_onWakeup_canNotDream_glanceableHubAvailable() =
+ testScope.runTest {
+ whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(false)
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // If dreaming is NOT possible but communal is available, then we should transition to
+ // LOCKSCREEN when waking up.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionToLockscreen_onWakeup_canNDream_glanceableHubNotAvailable() =
+ testScope.runTest {
+ whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
+ kosmos.setCommunalAvailable(false)
+ runCurrent()
+
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ // If dreaming is possible but communal is NOT available, then we should transition to
+ // LOCKSCREEN when waking up.
+ assertThat(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() =
testScope.runTest {
kosmos.fakeCommunalSceneRepository.setTransitionState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
similarity index 92%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
index 9d34903..96b4b43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.shade.pulsingGestureListener
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
@@ -53,14 +54,14 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-class KeyguardLongPressInteractorTest : SysuiTestCase() {
+class KeyguardTouchHandlingInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
this.accessibilityManagerWrapper = mock<AccessibilityManagerWrapper>()
this.uiEventLogger = mock<UiEventLoggerFake>()
}
- private lateinit var underTest: KeyguardLongPressInteractor
+ private lateinit var underTest: KeyguardTouchHandlingInteractor
private val logger = kosmos.uiEventLogger
private val testScope = kosmos.testScope
@@ -209,7 +210,7 @@
underTest.onLongPress()
assertThat(isMenuVisible).isTrue()
- advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
+ advanceTimeBy(KeyguardTouchHandlingInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
assertThat(isMenuVisible).isFalse()
}
@@ -224,11 +225,11 @@
assertThat(isMenuVisible).isTrue()
underTest.onMenuTouchGestureStarted()
- advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
+ advanceTimeBy(KeyguardTouchHandlingInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
assertThat(isMenuVisible).isTrue()
underTest.onMenuTouchGestureEnded(/* isClick= */ false)
- advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
+ advanceTimeBy(KeyguardTouchHandlingInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
assertThat(isMenuVisible).isFalse()
}
@@ -241,7 +242,7 @@
underTest.onLongPress()
verify(logger)
- .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
+ .log(KeyguardTouchHandlingInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
}
@Test
@@ -254,7 +255,7 @@
underTest.onMenuTouchGestureEnded(/* isClick= */ true)
verify(logger)
- .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
+ .log(KeyguardTouchHandlingInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
}
@Test
@@ -288,7 +289,7 @@
// This needs to be re-created for each test outside of kosmos since the flag values are
// read during initialization to set up flows. Maybe there is a better way to handle that.
underTest =
- KeyguardLongPressInteractor(
+ KeyguardTouchHandlingInteractor(
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor = kosmos.keyguardTransitionInteractor,
@@ -300,7 +301,8 @@
set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, isOpenWppDirectlyEnabled)
},
broadcastDispatcher = fakeBroadcastDispatcher,
- accessibilityManager = kosmos.accessibilityManagerWrapper
+ accessibilityManager = kosmos.accessibilityManagerWrapper,
+ pulsingGestureListener = kosmos.pulsingGestureListener,
)
setUpState()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 519bb6e..63d06a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -55,6 +55,8 @@
@Mock private lateinit var burnInInteractor: BurnInInteractor
@Mock private lateinit var goneToAodTransitionViewModel: GoneToAodTransitionViewModel
+ @Mock
+ private lateinit var lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var clockController: ClockController
private val kosmos = testKosmos()
@@ -76,7 +78,12 @@
kosmos.burnInInteractor = burnInInteractor
whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
.thenReturn(emptyFlow())
+ whenever(goneToAodTransitionViewModel.enterFromSideTranslationX(anyInt()))
+ .thenReturn(emptyFlow())
+ whenever(lockscreenToAodTransitionViewModel.enterFromSideTranslationX(anyInt()))
+ .thenReturn(emptyFlow())
kosmos.goneToAodTransitionViewModel = goneToAodTransitionViewModel
+ kosmos.lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel
kosmos.fakeKeyguardClockRepository.setCurrentClock(clockController)
underTest = kosmos.aodBurnInViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 6d9c271..b49e546 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -227,6 +227,15 @@
assertThat(accessibilityDelegateHint)
.isEqualTo(DeviceEntryIconView.AccessibilityHintType.BOUNCER)
+ // udfps running
+ setUpState(
+ isUdfpsSupported = true,
+ isUdfpsRunning = true,
+ )
+
+ assertThat(accessibilityDelegateHint)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.BOUNCER)
+
// non-interactive lock icon
fingerprintPropertyRepository.supportsRearFps()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index 716c40d..bab466a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -28,6 +28,9 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.powerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
@@ -47,10 +50,18 @@
private val underTest = kosmos.goneToAodTransitionViewModel
private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
private val biometricSettingsRepository = kosmos.biometricSettingsRepository
+ private val powerRepository = kosmos.powerRepository
@Test
- fun enterFromTopTranslationY() =
+ fun enterFromTopTranslationY_whenNotOnFold() =
testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON,
+ powerButtonLaunchGestureTriggered = false
+ )
+
val pixels = -100f
val enterFromTopTranslationY by
collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
@@ -88,6 +99,80 @@
}
@Test
+ fun enterFromTopTranslationY_whenOnFold_emitsNothing() =
+ testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.FOLD,
+ powerButtonLaunchGestureTriggered = false
+ )
+
+ val pixels = -100f
+ val enterFromTopTranslationY by
+ collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(enterFromTopTranslationY).isNull()
+
+ repository.sendTransitionStep(step(.55f))
+ assertThat(enterFromTopTranslationY).isNull()
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(enterFromTopTranslationY).isNull()
+
+ repository.sendTransitionStep(step(1f))
+ assertThat(enterFromTopTranslationY).isNull()
+ }
+
+ @Test
+ fun enterFromSideTranslationX_onFold() =
+ testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.FOLD,
+ powerButtonLaunchGestureTriggered = false
+ )
+
+ val pixels = -100f
+ val enterFromSideTranslationX by
+ collectLastValue(underTest.enterFromSideTranslationX(pixels.toInt()))
+ runCurrent()
+
+ // The animation should only start > .4f way through
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(enterFromSideTranslationX)
+ .isEqualTo(
+ StateToValue(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ value = pixels
+ )
+ )
+
+ repository.sendTransitionStep(step(.55f))
+ assertThat(enterFromSideTranslationX!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
+
+ repository.sendTransitionStep(step(.85f))
+ assertThat(enterFromSideTranslationX!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
+
+ // At the end, the translation should be complete and set to zero
+ repository.sendTransitionStep(step(1f))
+ assertThat(enterFromSideTranslationX)
+ .isEqualTo(
+ StateToValue(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.RUNNING,
+ value = 0f
+ )
+ )
+ }
+
+ @Test
fun enterFromTopAnimationAlpha() =
testScope.runTest {
val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
index 3777e40..6f74ed3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -16,98 +16,108 @@
package com.android.systemui.keyguard.ui.viewmodel
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.keyguardBottomAreaInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyguardIndicationAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
- @Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel
-
- @Mock private lateinit var burnInInteractor: BurnInInteractor
- private val burnInFlow = MutableStateFlow(BurnInModel())
-
- private lateinit var bottomAreaInteractor: KeyguardBottomAreaInteractor
+ private val bottomAreaInteractor = kosmos.keyguardBottomAreaInteractor
private lateinit var underTest: KeyguardIndicationAreaViewModel
- private lateinit var repository: FakeKeyguardRepository
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val communalSceneRepository = kosmos.fakeCommunalSceneRepository
private val startButtonFlow =
- MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ MutableStateFlow(
KeyguardQuickAffordanceViewModel(
slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
)
)
private val endButtonFlow =
- MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ MutableStateFlow(
KeyguardQuickAffordanceViewModel(
slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
)
)
- private val alphaFlow = MutableStateFlow<Float>(1f)
+ private val alphaFlow = MutableStateFlow(1f)
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-
- whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
- .thenReturn(RETURNED_BURN_IN_OFFSET)
- whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow)
-
- val withDeps = KeyguardInteractorFactory.create()
- val keyguardInteractor = withDeps.keyguardInteractor
- repository = withDeps.repository
-
- val bottomAreaViewModel: KeyguardBottomAreaViewModel = mock()
- whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow)
- whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow)
- whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow)
- bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository)
+ val bottomAreaViewModel =
+ mock<KeyguardBottomAreaViewModel> {
+ on { startButton } doReturn startButtonFlow
+ on { endButton } doReturn endButtonFlow
+ on { alpha } doReturn alphaFlow
+ }
+ val burnInInteractor =
+ mock<BurnInInteractor> {
+ on { burnIn(anyInt(), anyInt()) } doReturn flowOf(BurnInModel())
+ }
+ val burnInHelperWrapper =
+ mock<BurnInHelperWrapper> {
+ on { burnInOffset(anyInt(), any()) } doReturn RETURNED_BURN_IN_OFFSET
+ }
+ val shortcutsCombinedViewModel =
+ mock<KeyguardQuickAffordancesCombinedViewModel> {
+ on { startButton } doReturn startButtonFlow
+ on { endButton } doReturn endButtonFlow
+ }
underTest =
KeyguardIndicationAreaViewModel(
- keyguardInteractor = keyguardInteractor,
+ keyguardInteractor = kosmos.keyguardInteractor,
bottomAreaInteractor = bottomAreaInteractor,
keyguardBottomAreaViewModel = bottomAreaViewModel,
burnInHelperWrapper = burnInHelperWrapper,
burnInInteractor = burnInInteractor,
shortcutsCombinedViewModel = shortcutsCombinedViewModel,
- configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
+ configurationInteractor = kosmos.configurationInteractor,
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
- backgroundCoroutineContext = kosmos.testDispatcher,
+ backgroundDispatcher = kosmos.testDispatcher,
+ communalSceneInteractor = kosmos.communalSceneInteractor,
mainDispatcher = kosmos.testDispatcher
)
}
@@ -115,77 +125,120 @@
@Test
fun alpha() =
testScope.runTest {
- val value = collectLastValue(underTest.alpha)
+ val alpha by collectLastValue(underTest.alpha)
- assertThat(value()).isEqualTo(1f)
+ assertThat(alpha).isEqualTo(1f)
alphaFlow.value = 0.1f
- assertThat(value()).isEqualTo(0.1f)
+ assertThat(alpha).isEqualTo(0.1f)
alphaFlow.value = 0.5f
- assertThat(value()).isEqualTo(0.5f)
+ assertThat(alpha).isEqualTo(0.5f)
alphaFlow.value = 0.2f
- assertThat(value()).isEqualTo(0.2f)
+ assertThat(alpha).isEqualTo(0.2f)
alphaFlow.value = 0f
- assertThat(value()).isEqualTo(0f)
+ assertThat(alpha).isEqualTo(0f)
}
@Test
+ @DisableFlags(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
fun isIndicationAreaPadded() =
testScope.runTest {
- repository.setKeyguardShowing(true)
- val value = collectLastValue(underTest.isIndicationAreaPadded)
+ keyguardRepository.setKeyguardShowing(true)
+ val isIndicationAreaPadded by collectLastValue(underTest.isIndicationAreaPadded)
- assertThat(value()).isFalse()
+ assertThat(isIndicationAreaPadded).isFalse()
startButtonFlow.value = startButtonFlow.value.copy(isVisible = true)
- assertThat(value()).isTrue()
+ assertThat(isIndicationAreaPadded).isTrue()
endButtonFlow.value = endButtonFlow.value.copy(isVisible = true)
- assertThat(value()).isTrue()
+ assertThat(isIndicationAreaPadded).isTrue()
startButtonFlow.value = startButtonFlow.value.copy(isVisible = false)
- assertThat(value()).isTrue()
+ assertThat(isIndicationAreaPadded).isTrue()
endButtonFlow.value = endButtonFlow.value.copy(isVisible = false)
- assertThat(value()).isFalse()
+ assertThat(isIndicationAreaPadded).isFalse()
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
fun indicationAreaTranslationX() =
testScope.runTest {
- val value = collectLastValue(underTest.indicationAreaTranslationX)
+ val translationX by collectLastValue(underTest.indicationAreaTranslationX)
- assertThat(value()).isEqualTo(0f)
+ assertThat(translationX).isEqualTo(0f)
bottomAreaInteractor.setClockPosition(100, 100)
- assertThat(value()).isEqualTo(100f)
+ assertThat(translationX).isEqualTo(100f)
bottomAreaInteractor.setClockPosition(200, 100)
- assertThat(value()).isEqualTo(200f)
+ assertThat(translationX).isEqualTo(200f)
bottomAreaInteractor.setClockPosition(200, 200)
- assertThat(value()).isEqualTo(200f)
+ assertThat(translationX).isEqualTo(200f)
bottomAreaInteractor.setClockPosition(300, 100)
- assertThat(value()).isEqualTo(300f)
+ assertThat(translationX).isEqualTo(300f)
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun indicationAreaTranslationY() =
testScope.runTest {
- val value =
+ val translationY by
collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
// Negative 0 - apparently there's a difference in floating point arithmetic - FML
- assertThat(value()).isEqualTo(-0f)
+ assertThat(translationY).isEqualTo(-0f)
val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
- assertThat(value()).isEqualTo(expected1)
+ assertThat(translationY).isEqualTo(expected1)
val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
- assertThat(value()).isEqualTo(expected2)
+ assertThat(translationY).isEqualTo(expected2)
val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
- assertThat(value()).isEqualTo(expected3)
+ assertThat(translationY).isEqualTo(expected3)
val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
- assertThat(value()).isEqualTo(expected4)
+ assertThat(translationY).isEqualTo(expected4)
+ }
+
+ @Test
+ fun visibilityWhenCommunalNotShowing() =
+ testScope.runTest {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ val visible by collectLastValue(underTest.visible)
+
+ assertThat(visible).isTrue()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun visibilityWhenCommunalShowing() =
+ testScope.runTest {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ communalSceneRepository.setTransitionState(
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
+ )
+
+ val visible by collectLastValue(underTest.visible)
+
+ assertThat(visible).isTrue()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ assertThat(visible).isTrue()
+
+ communalSceneRepository.setTransitionState(
+ flowOf(ObservableTransitionState.Idle(CommunalScenes.Blank))
+ )
+ assertThat(visible).isFalse()
}
private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
- repository.setDozeAmount(dozeAmount)
+ keyguardRepository.setDozeAmount(dozeAmount)
return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
}
companion object {
private const val DEFAULT_BURN_IN_OFFSET = 5
private const val RETURNED_BURN_IN_OFFSET = 3
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
+ FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
+ )
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 61d8216..4eb146d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.SceneKey
@@ -61,6 +62,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
+@RunWithLooper
@EnableSceneContainer
class LockscreenSceneViewModelTest : SysuiTestCase() {
@@ -265,8 +267,8 @@
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = kosmos.deviceEntryInteractor,
communalInteractor = kosmos.communalInteractor,
- longPress =
- KeyguardLongPressViewModel(
+ touchHandling =
+ KeyguardTouchHandlingViewModel(
interactor = mock(),
),
notifications = kosmos.notificationsPlaceholderViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index bc0512a..24672eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -30,11 +30,21 @@
import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.testKosmos
+import com.android.systemui.util.time.systemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -42,8 +52,20 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
+ private val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+ private val mediaRecommendation =
+ SmartspaceMediaData(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ recommendations = MediaTestHelper.getValidRecommendationList(icon),
+ )
- private val underTest: MediaFilterRepository = kosmos.mediaFilterRepository
+ private val underTest: MediaFilterRepository =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaFilterRepository
+ }
@Test
fun addSelectedUserMediaEntry_activeThenInactivate() =
@@ -137,14 +159,6 @@
testScope.runTest {
val smartspaceMediaData by collectLastValue(underTest.smartspaceMediaData)
- val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
- val mediaRecommendation =
- SmartspaceMediaData(
- targetId = KEY_MEDIA_SMARTSPACE,
- isActive = true,
- recommendations = MediaTestHelper.getValidRecommendationList(icon),
- )
-
underTest.setRecommendation(mediaRecommendation)
assertThat(smartspaceMediaData).isEqualTo(mediaRecommendation)
@@ -164,16 +178,44 @@
val playingData = createMediaData("app1", true, LOCAL, false, playingInstanceId)
val remoteData = createMediaData("app2", true, REMOTE, false, remoteInstanceId)
+ underTest.setRecommendation(mediaRecommendation)
+ underTest.setRecommendationsLoadingState(
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
+ )
underTest.addSelectedUserMediaEntry(playingData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId))
- underTest.addSelectedUserMediaEntry(remoteData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(remoteInstanceId))
+ underTest.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(playingInstanceId),
+ false
+ )
- assertThat(currentMedia?.size).isEqualTo(2)
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(
+ playingData.smartspaceId,
+ playingData.appUid,
+ cardinality = 2
+ )
+
+ underTest.addSelectedUserMediaEntry(remoteData)
+ underTest.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(remoteInstanceId),
+ false
+ )
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(
+ remoteData.smartspaceId,
+ playingData.appUid,
+ cardinality = 3,
+ rank = 1
+ )
+ assertThat(currentMedia?.size).isEqualTo(3)
assertThat(currentMedia)
.containsExactly(
MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId)),
- MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(remoteInstanceId))
+ MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(remoteInstanceId)),
+ MediaCommonModel.MediaRecommendations(
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
+ )
)
.inOrder()
}
@@ -222,6 +264,16 @@
underTest.setOrderedMedia()
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardReceived(
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt()
+ )
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
.containsExactly(
@@ -248,14 +300,6 @@
val stoppedAndRemoteData = createMediaData("app4", false, REMOTE, false, instanceId4)
val canResumeData = createMediaData("app5", false, LOCAL, true, instanceId5)
- val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
- val mediaRecommendations =
- SmartspaceMediaData(
- targetId = KEY_MEDIA_SMARTSPACE,
- isActive = true,
- recommendations = MediaTestHelper.getValidRecommendationList(icon),
- )
-
underTest.addSelectedUserMediaEntry(stoppedAndLocalData)
underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId3))
@@ -271,11 +315,33 @@
underTest.addSelectedUserMediaEntry(playingAndRemoteData)
underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId2))
- underTest.setRecommendation(mediaRecommendations)
+ underTest.setRecommendation(mediaRecommendation)
underTest.setRecommendationsLoadingState(
SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
)
+ underTest.setOrderedMedia()
+ val smartspaceId = SmallHash.hash(mediaRecommendation.targetId)
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(
+ eq(smartspaceId),
+ anyInt(),
+ eq(6),
+ anyBoolean(),
+ anyBoolean(),
+ eq(2),
+ anyInt()
+ )
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardReceived(
+ eq(playingAndLocalData.smartspaceId),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt()
+ )
assertThat(currentMedia?.size).isEqualTo(6)
assertThat(currentMedia)
.containsExactly(
@@ -312,18 +378,10 @@
isPlaying = true,
notificationKey = KEY_2
)
- val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
- val mediaRecommendations =
- SmartspaceMediaData(
- targetId = KEY_MEDIA_SMARTSPACE,
- isActive = true,
- packageName = PACKAGE_NAME,
- recommendations = MediaTestHelper.getValidRecommendationList(icon),
- )
underTest.setMediaFromRecPackageName(PACKAGE_NAME)
underTest.addSelectedUserMediaEntry(data)
- underTest.setRecommendation(mediaRecommendations)
+ underTest.setRecommendation(mediaRecommendation)
underTest.setRecommendationsLoadingState(
SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
)
@@ -365,6 +423,89 @@
fun hasActiveMedia_noMediaSet_returnsFalse() =
testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() }
+ @Test
+ fun updateMediaWithLatency_smartspaceIsLogged() =
+ testScope.runTest {
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val data = createMediaData("app", true, LOCAL, false, instanceId)
+
+ underTest.setRecommendation(mediaRecommendation)
+ underTest.setRecommendationsLoadingState(
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
+ )
+
+ val smartspaceId = SmallHash.hash(mediaRecommendation.targetId)
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(
+ eq(smartspaceId),
+ anyInt(),
+ eq(1),
+ eq(true),
+ anyBoolean(),
+ eq(0),
+ anyInt()
+ )
+ reset(smartspaceLogger)
+
+ underTest.addSelectedUserMediaEntry(data)
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId), false)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(data.smartspaceId, data.appUid, cardinality = 2)
+
+ reset(smartspaceLogger)
+
+ underTest.addSelectedUserMediaEntry(data)
+ underTest.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(instanceId, receivedSmartspaceCardLatency = 123),
+ true
+ )
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(
+ SmallHash.hash(data.appUid + kosmos.systemClock.currentTimeMillis().toInt()),
+ data.appUid,
+ cardinality = 2,
+ rank = 0,
+ receivedLatencyMillis = 123
+ )
+ }
+
+ @Test
+ fun resumeMedia_loadSmartspace_allSmartspaceIsLogged() =
+ testScope.runTest {
+ val resumeInstanceId = InstanceId.fakeInstanceId(123)
+ val data = createMediaData("app", false, LOCAL, true, resumeInstanceId)
+
+ underTest.addSelectedUserMediaEntry(data.copy(active = false))
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(resumeInstanceId))
+ underTest.setRecommendation(mediaRecommendation)
+ underTest.setRecommendationsLoadingState(
+ SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
+ )
+
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isTrue()
+ val smartspaceId = SmallHash.hash(mediaRecommendation.targetId)
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(
+ eq(smartspaceId),
+ anyInt(),
+ eq(2),
+ eq(true),
+ anyBoolean(),
+ eq(0),
+ anyInt()
+ )
+ verify(smartspaceLogger)
+ .logSmartspaceCardReceived(
+ SmallHash.hash(data.appUid + kosmos.systemClock.currentTimeMillis().toInt()),
+ data.appUid,
+ cardinality = 2,
+ rank = 1
+ )
+ }
+
private fun createMediaData(
app: String,
playing: Boolean,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index c62195f..414974c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -18,6 +18,7 @@
import android.R
import android.graphics.drawable.Icon
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
@@ -38,12 +39,19 @@
import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -52,7 +60,11 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
+ private val mediaFilterRepository: MediaFilterRepository =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaFilterRepository
+ }
private val mediaRecommendationsInteractor: MediaRecommendationsInteractor =
kosmos.mediaRecommendationsInteractor
val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
@@ -63,6 +75,7 @@
packageName = PACKAGE_NAME,
recommendations = MediaTestHelper.getValidRecommendationList(icon),
)
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor
@@ -153,6 +166,18 @@
MediaCommonModel.MediaControl(mediaLoadingModel, true)
)
.inOrder()
+
+ underTest.logSmartspaceSeenCard(0, 1, false)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ SmallHash.hash(mediaRecommendation.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ 2,
+ true
+ )
}
@Test
@@ -239,7 +264,7 @@
.inOrder()
mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true))
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
assertThat(currentMedia)
.containsExactly(
@@ -249,9 +274,83 @@
.inOrder()
}
+ @Test
+ fun loadMediaAndRecommendation_logSmartspaceSeenCard() {
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val data =
+ MediaData(
+ active = true,
+ instanceId = instanceId,
+ packageName = PACKAGE_NAME,
+ notificationKey = KEY
+ )
+ val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+ val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)
+
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ underTest.logSmartspaceSeenCard(0, 1, false)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 1
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ underTest.logSmartspaceSeenCard(0, 1, true)
+
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 2
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.setRecommendation(mediaRecommendation)
+ mediaFilterRepository.setRecommendationsLoadingState(smartspaceLoadingModel)
+ underTest.logSmartspaceSeenCard(1, 1, true)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ SmallHash.hash(mediaRecommendation.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ 2,
+ true,
+ rank = 1
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(
+ mediaLoadingModel.copy(receivedSmartspaceCardLatency = 1)
+ )
+ underTest.logSmartspaceSeenCard(0, 1, true)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 2
+ )
+ }
+
companion object {
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.android.example"
private const val KEY = "key"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index 856c3fe..d594f3a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.media.controls.domain.interactor
+import android.R
import android.app.PendingIntent
+import android.graphics.drawable.Icon
import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -30,6 +32,7 @@
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.MediaTestHelper
import com.android.systemui.media.controls.data.repository.mediaDataRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
@@ -38,7 +41,12 @@
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.media.mediaOutputDialogManager
import com.android.systemui.mockActivityIntentHelper
import com.android.systemui.plugins.activityStarter
@@ -49,6 +57,8 @@
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
@@ -63,11 +73,23 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val mediaDataFilter: MediaDataFilterImpl =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaDataFilter
+ }
private val activityStarter = kosmos.activityStarter
private val keyguardStateController = kosmos.keyguardStateController
private val instanceId: InstanceId = kosmos.mediaInstanceId
private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
+ private val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+ private val mediaRecommendation =
+ SmartspaceMediaData(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ recommendations = MediaTestHelper.getValidRecommendationList(icon),
+ )
private val underTest: MediaControlInteractor =
with(kosmos) {
@@ -124,13 +146,15 @@
val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
val expandable = mock<Expandable>()
- underTest.startClickIntent(expandable, clickIntent)
+ underTest.startClickIntent(expandable, clickIntent, SMARTSPACE_CARD_CLICK_EVENT, 1)
verify(clickIntent).send(any<Bundle>())
}
@Test
fun startClickIntent_hideOverLockscreen() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
whenever(keyguardStateController.isShowing).thenReturn(false)
val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
@@ -138,8 +162,20 @@
val activityController = mock<ActivityTransitionAnimator.Controller>()
whenever(expandable.activityTransitionController(any())).thenReturn(activityController)
- underTest.startClickIntent(expandable, clickIntent)
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, mediaRecommendation, true)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
+ underTest.startClickIntent(expandable, clickIntent, SMARTSPACE_CARD_CLICK_EVENT, 1)
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_CLICK_EVENT,
+ mediaData.smartspaceId,
+ mediaData.appUid,
+ surface = SURFACE,
+ cardinality = 2,
+ rank = 1
+ )
verify(activityStarter)
.postStartActivityDismissingKeyguard(eq(clickIntent), eq(activityController))
}
@@ -217,17 +253,62 @@
}
@Test
- fun removeMediaControl() {
+ fun removeMediaControl_noRecommendation() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val listener = mock<MediaDataProcessor.Listener>()
kosmos.mediaDataProcessor.addInternalListener(listener)
- var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+ kosmos.mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
- underTest.removeMediaControl(null, instanceId, 0L)
+ underTest.removeMediaControl(null, instanceId, 0L, SMARTSPACE_CARD_DISMISS_EVENT, 1)
kosmos.fakeExecutor.advanceClockToNext()
kosmos.fakeExecutor.runAllReady()
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean()
+ )
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
+ }
+
+ @Test
+ fun removeMediaControl_recommendationsExist() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+ val listener = mock<MediaDataProcessor.Listener>()
+ kosmos.mediaDataProcessor.addInternalListener(listener)
+
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, mediaRecommendation, true)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
+
+ underTest.removeMediaControl(null, instanceId, 0L, SMARTSPACE_CARD_DISMISS_EVENT, 1)
+ kosmos.fakeExecutor.advanceClockToNext()
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ mediaData.smartspaceId,
+ mediaData.appUid,
+ surface = SURFACE,
+ cardinality = 2,
+ rank = 1
+ )
verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
}
@@ -238,5 +319,7 @@
private const val APP_NAME = "app"
private const val ARTIST = "artist"
private const val ARTIST_2 = "artist2"
+ private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
index 9656511..8af7e1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
@@ -21,6 +21,7 @@
import android.content.Intent
import android.content.applicationContext
import android.graphics.drawable.Icon
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -40,10 +41,14 @@
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.plugins.activityStarter
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -53,6 +58,7 @@
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.kotlin.eq
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -62,7 +68,11 @@
private val kosmos = testKosmos().apply { applicationContext = spyContext }
private val testScope = kosmos.testScope
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val mediaDataFilter: MediaDataFilterImpl =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaDataFilter
+ }
private val activityStarter = kosmos.activityStarter
private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play)
private val smartspaceMediaData: SmartspaceMediaData =
@@ -72,6 +82,7 @@
packageName = PACKAGE_NAME,
recommendations = MediaTestHelper.getValidRecommendationList(icon),
)
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
private val underTest: MediaRecommendationsInteractor =
with(kosmos) {
@@ -138,8 +149,24 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
+ underTest.removeMediaRecommendations(
+ KEY_MEDIA_SMARTSPACE,
+ intent,
+ 0,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ 1
+ )
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ SmallHash.hash(smartspaceMediaData.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ cardinality = 1,
+ isRecommendationCard = true,
+ )
verify(kosmos.mockBroadcastSender).sendBroadcast(eq(intent))
}
@@ -151,7 +178,13 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.component = ComponentName(PACKAGE_NAME, EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
+ underTest.removeMediaRecommendations(
+ KEY_MEDIA_SMARTSPACE,
+ intent,
+ 0,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ 1
+ )
verify(spyContext).startActivity(eq(intent))
}
@@ -171,13 +204,26 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- underTest.startClickIntent(expandable, intent)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
+ underTest.startClickIntent(expandable, intent, SMARTSPACE_CARD_CLICK_EVENT, 1, 2, 3)
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_CLICK_EVENT,
+ SmallHash.hash(smartspaceMediaData.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ cardinality = 1,
+ isRecommendationCard = true,
+ interactedSubcardRank = 2,
+ interactedSubcardCardinality = 3
+ )
verify(spyContext).startActivity(eq(intent))
}
companion object {
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.example.app"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
index 71685a4..005424b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
@@ -47,7 +47,7 @@
oldList,
newList,
{ commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaControl) },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -66,7 +66,7 @@
oldList,
newList,
{ commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -85,7 +85,7 @@
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
+ { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -104,7 +104,7 @@
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) },
+ { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -124,7 +124,7 @@
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaControl1) },
)
@@ -145,7 +145,7 @@
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
)
@@ -164,7 +164,7 @@
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaControl) },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -183,7 +183,7 @@
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/data/repository/NavigationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/data/repository/NavigationRepositoryTest.kt
new file mode 100644
index 0000000..e45aa05
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/data/repository/NavigationRepositoryTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.navigation.data.repository
+
+import android.view.WindowManagerPolicyConstants
+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.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.navigationbar.navigationModeController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavigationRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val navigationModeControllerMock = kosmos.navigationModeController
+
+ private val underTest = kosmos.navigationRepository
+
+ private var currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ private val modeChangedListeners = mutableListOf<ModeChangedListener>()
+
+ @Before
+ fun setUp() {
+ whenever(navigationModeControllerMock.addListener(any())).thenAnswer { invocation ->
+ val listener = invocation.arguments[0] as ModeChangedListener
+ modeChangedListeners.add(listener)
+ currentMode
+ }
+ }
+
+ @Test
+ fun isGesturalMode() =
+ testScope.runTest {
+ val isGesturalMode by collectLastValue(underTest.isGesturalMode)
+ assertThat(isGesturalMode).isFalse()
+
+ currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+ notifyModeChangedListeners()
+ assertThat(isGesturalMode).isTrue()
+
+ currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ notifyModeChangedListeners()
+ assertThat(isGesturalMode).isFalse()
+ }
+
+ private fun notifyModeChangedListeners() {
+ modeChangedListeners.forEach { listener -> listener.onNavigationModeChanged(currentMode) }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorTest.kt
new file mode 100644
index 0000000..88beeb2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.navigation.domain.interactor
+
+import android.view.WindowManagerPolicyConstants
+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.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.navigationbar.navigationModeController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavigationInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val navigationModeControllerMock = kosmos.navigationModeController
+
+ private val underTest = kosmos.navigationInteractor
+
+ private var currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ private val modeChangedListeners = mutableListOf<ModeChangedListener>()
+
+ @Before
+ fun setUp() {
+ whenever(navigationModeControllerMock.addListener(any())).thenAnswer { invocation ->
+ val listener = invocation.arguments[0] as ModeChangedListener
+ modeChangedListeners.add(listener)
+ currentMode
+ }
+ }
+
+ @Test
+ fun isGesturalMode() =
+ testScope.runTest {
+ val isGesturalMode by collectLastValue(underTest.isGesturalMode)
+ assertThat(isGesturalMode).isFalse()
+
+ currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+ notifyModeChangedListeners()
+ assertThat(isGesturalMode).isTrue()
+
+ currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ notifyModeChangedListeners()
+ assertThat(isGesturalMode).isFalse()
+ }
+
+ private fun notifyModeChangedListeners() {
+ modeChangedListeners.forEach { listener -> listener.onNavigationModeChanged(currentMode) }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
index cb4e2d3..16c7090 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
@@ -28,12 +28,14 @@
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -56,7 +58,7 @@
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
- private val underTest = kosmos.notificationsShadeSceneViewModel
+ private val underTest by lazy { kosmos.notificationsShadeSceneViewModel }
@Test
fun upTransitionSceneKey_deviceLocked_lockscreen() =
@@ -65,11 +67,23 @@
lockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
.isEqualTo(Scenes.Lockscreen)
}
@Test
+ fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
+ testScope.runTest {
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ lockDevice()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
val destinationScenes by collectLastValue(underTest.destinationScenes)
@@ -77,6 +91,33 @@
unlockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ lockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
+ assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+ .isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ lockDevice()
+ unlockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
new file mode 100644
index 0000000..14d6094
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PaginatedGridRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ val underTest = kosmos.paginatedGridRepository
+
+ @Test
+ fun rows_followsConfig() =
+ with(kosmos) {
+ testScope.runTest {
+ val rows by collectLastValue(underTest.rows)
+
+ setRowsInConfig(3)
+ assertThat(rows).isEqualTo(3)
+
+ setRowsInConfig(6)
+ assertThat(rows).isEqualTo(6)
+ }
+ }
+
+ private fun setRowsInConfig(rows: Int) =
+ with(kosmos) {
+ testCase.context.orCreateTestableResources.addOverride(
+ R.integer.quick_settings_max_rows,
+ rows,
+ )
+ fakeConfigurationRepository.onConfigurationChange()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
index b0aa6dd..8ac5b6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data
+package com.android.systemui.qs.panels.data.repository
import android.content.Context
import android.content.SharedPreferences
import android.content.pm.UserInfo
-import android.testing.AndroidTestingRunner
+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.qs.panels.data.repository.QSPreferencesRepository
-import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
import com.android.systemui.settings.userFileManager
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -36,7 +34,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class QSPreferencesRepositoryTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val underTest = with(kosmos) { qsPreferencesRepository }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
index 9b08432e..7ad904e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.qs.panels.domain.interactor
import android.content.pm.UserInfo
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -34,7 +34,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class IconLabelVisibilityInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val underTest = with(kosmos) { iconLabelVisibilityInteractor }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
new file mode 100644
index 0000000..914a095
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayoutTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.IconTilesRepository
+import com.android.systemui.qs.panels.data.repository.iconTilesRepository
+import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.fixedColumnsSizeViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class InfiniteGridLayoutTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ iconTilesRepository =
+ object : IconTilesRepository {
+ override fun isIconTile(spec: TileSpec): Boolean {
+ return spec.spec.startsWith("small")
+ }
+ }
+ }
+
+ private val underTest =
+ with(kosmos) {
+ InfiniteGridLayout(
+ iconTilesViewModel,
+ fixedColumnsSizeViewModel,
+ )
+ }
+
+ @Test
+ fun correctPagination_underOnePage_sameOrder() =
+ with(kosmos) {
+ testScope.runTest {
+ val rows = 3
+ val columns = 4
+
+ val tiles =
+ listOf(
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ largeTile(),
+ largeTile(),
+ smallTile()
+ )
+
+ val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
+
+ assertThat(pages).hasSize(1)
+ assertThat(pages[0]).isEqualTo(tiles)
+ }
+ }
+
+ @Test
+ fun correctPagination_twoPages_sameOrder() =
+ with(kosmos) {
+ testScope.runTest {
+ val rows = 3
+ val columns = 4
+
+ val tiles =
+ listOf(
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ largeTile(),
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ largeTile(),
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ largeTile(),
+ )
+ // --- Page 1 ---
+ // [L L] [S] [S]
+ // [L L] [L L]
+ // [S] [S] [L L]
+ // --- Page 2 ---
+ // [L L] [S] [S]
+ // [L L]
+
+ val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
+
+ assertThat(pages).hasSize(2)
+ assertThat(pages[0]).isEqualTo(tiles.take(8))
+ assertThat(pages[1]).isEqualTo(tiles.drop(8))
+ }
+ }
+
+ companion object {
+ fun largeTile() = MockTileViewModel(TileSpec.create("large"))
+
+ fun smallTile() = MockTileViewModel(TileSpec.create("small"))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt
new file mode 100644
index 0000000..6df3f8d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PaginatableGridLayoutTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PaginatableGridLayoutTest : SysuiTestCase() {
+ @Test
+ fun correctRows_gapsAtEnd() {
+ val columns = 6
+
+ val sizedTiles =
+ listOf(
+ largeTile(),
+ extraLargeTile(),
+ largeTile(),
+ smallTile(),
+ largeTile(),
+ )
+
+ // [L L] [XL XL XL]
+ // [L L] [S] [L L]
+
+ val rows = PaginatableGridLayout.splitInRows(sizedTiles, columns)
+
+ assertThat(rows).hasSize(2)
+ assertThat(rows[0]).isEqualTo(sizedTiles.take(2))
+ assertThat(rows[1]).isEqualTo(sizedTiles.drop(2))
+ }
+
+ @Test
+ fun correctRows_fullLastRow_noEmptyRow() {
+ val columns = 6
+
+ val sizedTiles =
+ listOf(
+ largeTile(),
+ extraLargeTile(),
+ smallTile(),
+ )
+
+ // [L L] [XL XL XL] [S]
+
+ val rows = PaginatableGridLayout.splitInRows(sizedTiles, columns)
+
+ assertThat(rows).hasSize(1)
+ assertThat(rows[0]).isEqualTo(sizedTiles)
+ }
+
+ companion object {
+ fun extraLargeTile() = SizedTile(MockTileViewModel(TileSpec.create("XLarge")), 3)
+
+ fun largeTile() = SizedTile(MockTileViewModel(TileSpec.create("large")), 2)
+
+ fun smallTile() = SizedTile(MockTileViewModel(TileSpec.create("small")), 1)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayoutTest.kt
new file mode 100644
index 0000000..3354b4d4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayoutTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.IconTilesRepository
+import com.android.systemui.qs.panels.data.repository.iconTilesRepository
+import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.partitionedGridViewModel
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PartitionedGridLayoutTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ iconTilesRepository =
+ object : IconTilesRepository {
+ override fun isIconTile(spec: TileSpec): Boolean {
+ return spec.spec.startsWith("small")
+ }
+ }
+ }
+
+ private val underTest = with(kosmos) { PartitionedGridLayout(partitionedGridViewModel) }
+
+ @Test
+ fun correctPagination_underOnePage_partitioned_sameRelativeOrder() =
+ with(kosmos) {
+ testScope.runTest {
+ val rows = 3
+ val columns = 4
+
+ val tiles =
+ listOf(
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ largeTile(),
+ largeTile(),
+ smallTile()
+ )
+ val (smallTiles, largeTiles) =
+ tiles.partition { iconTilesViewModel.isIconTile(it.spec) }
+
+ // [L L] [L L]
+ // [L L]
+ // [S] [S] [S]
+
+ val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
+
+ Truth.assertThat(pages).hasSize(1)
+ Truth.assertThat(pages[0]).isEqualTo(largeTiles + smallTiles)
+ }
+ }
+
+ @Test
+ fun correctPagination_twoPages_partitioned_sameRelativeOrder() =
+ with(kosmos) {
+ testScope.runTest {
+ val rows = 3
+ val columns = 4
+
+ val tiles =
+ listOf(
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ largeTile(),
+ smallTile(),
+ smallTile(),
+ )
+ // --- Page 1 ---
+ // [L L] [L L]
+ // [L L]
+ // [S] [S] [S] [S]
+ // --- Page 2 ---
+ // [S] [S]
+
+ val (smallTiles, largeTiles) =
+ tiles.partition { iconTilesViewModel.isIconTile(it.spec) }
+
+ val pages = underTest.splitIntoPages(tiles, rows = rows, columns = columns)
+
+ val expectedPage0 = largeTiles + smallTiles.take(4)
+ val expectedPage1 = smallTiles.drop(4)
+
+ Truth.assertThat(pages).hasSize(2)
+ Truth.assertThat(pages[0]).isEqualTo(expectedPage0)
+ Truth.assertThat(pages[1]).isEqualTo(expectedPage1)
+ }
+ }
+
+ companion object {
+ fun largeTile() = MockTileViewModel(TileSpec.create("large"))
+
+ fun smallTile() = MockTileViewModel(TileSpec.create("small"))
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
index 9bb591e..8ad647d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.CastTile
import com.android.systemui.statusbar.policy.CastController
+import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -73,9 +74,12 @@
@Test
fun onCastDevicesChanged_deviceNotConnectedOrConnecting_noSignal() = runTest {
val device =
- CastController.CastDevice().apply {
- state = CastController.CastDevice.STATE_DISCONNECTED
- }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
whenever(castController.castDevices).thenReturn(listOf(device))
val signal by collectLastValue(underTest.autoAddSignal(0))
@@ -91,11 +95,19 @@
@Test
fun onCastDevicesChanged_someDeviceConnecting_addSignal() = runTest {
val disconnectedDevice =
- CastController.CastDevice().apply {
- state = CastController.CastDevice.STATE_DISCONNECTED
- }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
val connectingDevice =
- CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTING }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Connecting,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
whenever(castController.castDevices)
.thenReturn(listOf(disconnectedDevice, connectingDevice))
@@ -112,11 +124,19 @@
@Test
fun onCastDevicesChanged_someDeviceConnected_addSignal() = runTest {
val disconnectedDevice =
- CastController.CastDevice().apply {
- state = CastController.CastDevice.STATE_DISCONNECTED
- }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
val connectedDevice =
- CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTED }
+ CastDevice(
+ id = "id",
+ name = null,
+ state = CastDevice.CastState.Connected,
+ origin = CastDevice.CastOrigin.MediaProjection,
+ )
whenever(castController.castDevices).thenReturn(listOf(disconnectedDevice, connectedDevice))
val signal by collectLastValue(underTest.autoAddSignal(0))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
new file mode 100644
index 0000000..2194c75
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.irecording
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.recordissue.IssueRecordingState
+import com.android.systemui.settings.fakeUserFileManager
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IssueRecordingDataInteractorTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos().also { it.testCase = this }
+ private val userTracker = kosmos.userTracker
+ private val userFileManager = kosmos.fakeUserFileManager
+ private val testUser = UserHandle.of(1)
+
+ lateinit var state: IssueRecordingState
+ private lateinit var underTest: IssueRecordingDataInteractor
+
+ @Before
+ fun setup() {
+ state = IssueRecordingState(userTracker, userFileManager)
+ underTest = IssueRecordingDataInteractor(state, kosmos.testScope.testScheduler)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun emitsEvent_whenIsRecordingStatusChanges_correctly() {
+ kosmos.testScope.runTest {
+ val data by
+ collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ Truth.assertThat(data?.isRecording).isFalse()
+
+ state.isRecording = true
+ runCurrent()
+ Truth.assertThat(data?.isRecording).isTrue()
+
+ state.isRecording = false
+ runCurrent()
+ Truth.assertThat(data?.isRecording).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
new file mode 100644
index 0000000..2444229
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.irecording
+
+import android.content.res.mainResources
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.recordissue.RecordIssueModule
+import com.android.systemui.res.R
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IssueRecordingMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos().also { it.testCase = this }
+ private val uiConfig =
+ QSTileUIConfig.Resource(R.drawable.qs_record_issue_icon_off, R.string.qs_record_issue_label)
+ private val config =
+ QSTileConfig(
+ TileSpec.create(RecordIssueModule.TILE_SPEC),
+ uiConfig,
+ kosmos.qsEventLogger.getNewInstanceId()
+ )
+ private val resources = kosmos.mainResources
+ private val theme = resources.newTheme()
+
+ @Test
+ fun whenData_isRecording_useCorrectResources() {
+ val underTest = IssueRecordingMapper(resources, theme)
+ val tileState = underTest.map(config, IssueRecordingModel(true))
+ Truth.assertThat(tileState.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
+ }
+
+ @Test
+ fun whenData_isNotRecording_useCorrectResources() {
+ val underTest = IssueRecordingMapper(resources, theme)
+ val tileState = underTest.map(config, IssueRecordingModel(false))
+ Truth.assertThat(tileState.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
new file mode 100644
index 0000000..4e58069
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.irecording
+
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.qs.pipeline.domain.interactor.panelInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.recordissue.RecordIssueDialogDelegate
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.google.common.truth.Truth
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IssueRecordingUserActionInteractorTest : SysuiTestCase() {
+
+ val user = UserHandle(1)
+ val kosmos = Kosmos().also { it.testCase = this }
+
+ private lateinit var userContextProvider: UserContextProvider
+ private lateinit var underTest: IssueRecordingUserActionInteractor
+
+ private var hasCreatedDialogDelegate: Boolean = false
+
+ @Before
+ fun setup() {
+ hasCreatedDialogDelegate = false
+ with(kosmos) {
+ val factory =
+ object : RecordIssueDialogDelegate.Factory {
+ override fun create(onStarted: Runnable): RecordIssueDialogDelegate {
+ hasCreatedDialogDelegate = true
+
+ // Inside some tests in presubmit, createDialog throws an error because
+ // the test thread's looper hasn't been prepared, and Dialog.class
+ // internally is creating a new handler. For testing, we only care that the
+ // dialog is created, so using a mock is acceptable here.
+ return mock(RecordIssueDialogDelegate::class.java)
+ }
+ }
+
+ userContextProvider = userTracker
+ underTest =
+ IssueRecordingUserActionInteractor(
+ testDispatcher,
+ KeyguardDismissUtil(
+ keyguardStateController,
+ statusBarStateController,
+ activityStarter
+ ),
+ keyguardStateController,
+ dialogTransitionAnimator,
+ panelInteractor,
+ userTracker,
+ factory
+ )
+ }
+ }
+
+ @Test
+ fun handleInput_showsPromptToStartRecording_whenNotRecordingAlready() {
+ kosmos.testScope.runTest {
+ underTest.handleInput(
+ QSTileInput(user, QSTileUserAction.Click(null), IssueRecordingModel(false))
+ )
+ Truth.assertThat(hasCreatedDialogDelegate).isTrue()
+ }
+ }
+
+ @Test
+ fun handleInput_attemptsToStopRecording_whenRecording() {
+ kosmos.testScope.runTest {
+ val input = QSTileInput(user, QSTileUserAction.Click(null), IssueRecordingModel(true))
+ try {
+ underTest.handleInput(input)
+ } catch (e: NullPointerException) {
+ // As of 06/07/2024, PendingIntent.startService is not easily mockable and throws
+ // an NPE inside IActivityManager. Catching that here and ignore it, then verify
+ // mock interactions were done correctly
+ }
+ Truth.assertThat(hasCreatedDialogDelegate).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 5b6fea5..9249621 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.ui.viewmodel
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
@@ -32,6 +33,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
@@ -42,9 +44,9 @@
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneBackInteractor
-import com.android.systemui.scene.domain.interactor.sceneContainerStartable
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
@@ -64,6 +66,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@RunWithLooper
@EnableSceneContainer
class QuickSettingsSceneViewModelTest : SysuiTestCase() {
@@ -160,6 +163,37 @@
}
@Test
+ fun destinations_whenNotCustomizing_withPreviousSceneLockscreen_butLockscreenDisabled() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ qsFlexiglassAdapter.setCustomizing(false)
+ val destinations by collectLastValue(underTest.destinationScenes)
+
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val backScene by collectLastValue(sceneBackInteractor.backScene)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+ sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(backScene).isNull()
+ assertThat(destinations)
+ .isEqualTo(
+ mapOf(
+ Back to UserActionResult(Scenes.Shade),
+ Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
+ Swipe(
+ fromSource = Edge.Bottom,
+ direction = SwipeDirection.Up,
+ ) to UserActionResult(SceneFamilies.Home)
+ )
+ )
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun destinations_whenNotCustomizing_authMethodSwipe_lockscreenNotDismissed() =
testScope.runTest {
overrideResource(R.bool.config_use_split_notification_shade, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
index ac67ac8..411a7a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
@@ -28,12 +28,14 @@
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.ui.viewmodel.quickSettingsShadeSceneViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -56,7 +58,7 @@
private val sceneInteractor = kosmos.sceneInteractor
private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
- private val underTest = kosmos.quickSettingsShadeSceneViewModel
+ private val underTest by lazy { kosmos.quickSettingsShadeSceneViewModel }
@Test
fun upTransitionSceneKey_deviceLocked_lockscreen() =
@@ -66,10 +68,23 @@
lockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
}
@Test
+ fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
+ testScope.runTest {
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ lockDevice()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
val destinationScenes by collectLastValue(underTest.destinationScenes)
@@ -78,6 +93,34 @@
unlockDevice()
assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Down)).isNull()
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ lockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
+ assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
+ testScope.runTest {
+ kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ lockDevice()
+ unlockDevice()
+
+ assertThat(destinationScenes?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
+ assertThat(destinationScenes?.get(Swipe.Up)).isNull()
assertThat(homeScene).isEqualTo(Scenes.Gone)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 4d5d22c..cb4d96f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -48,33 +48,28 @@
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.qs.footerActionsController
-import com.android.systemui.qs.footerActionsViewModelFactory
-import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.domain.interactor.sceneContainerStartable
+import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
-import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModel
+import com.android.systemui.shade.ui.viewmodel.shadeSceneViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
-import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -152,8 +147,8 @@
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
communalInteractor = communalInteractor,
- longPress =
- KeyguardLongPressViewModel(
+ touchHandling =
+ KeyguardTouchHandlingViewModel(
interactor = mock(),
),
notifications = kosmos.notificationsPlaceholderViewModel,
@@ -167,7 +162,7 @@
private var bouncerSceneJob: Job? = null
- private val qsFlexiglassAdapter = FakeQSSceneAdapter(inflateDelegate = { mock() })
+ private val qsFlexiglassAdapter = kosmos.fakeQSSceneAdapter
private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
private lateinit var telecomManager: TelecomManager
@@ -200,20 +195,7 @@
bouncerActionButtonInteractor = kosmos.bouncerActionButtonInteractor
bouncerViewModel = kosmos.bouncerViewModel
- shadeSceneViewModel =
- ShadeSceneViewModel(
- applicationScope = testScope.backgroundScope,
- shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
- qsSceneAdapter = qsFlexiglassAdapter,
- notifications = kosmos.notificationsPlaceholderViewModel,
- brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel,
- mediaCarouselInteractor = kosmos.mediaCarouselInteractor,
- shadeInteractor = kosmos.shadeInteractor,
- footerActionsController = kosmos.footerActionsController,
- footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
- sceneInteractor = sceneInteractor,
- unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor,
- )
+ shadeSceneViewModel = kosmos.shadeSceneViewModel
val startable = kosmos.sceneContainerStartable
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
index e3108ad..1f3454d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index ec7150b..5242fe3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.Idle
@@ -450,4 +451,16 @@
progress.value = 0.9f
assertThat(transitionValue).isEqualTo(0f)
}
+
+ @Test
+ fun changeScene_toGone_whenKeyguardDisabled_doesNotThrow() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ underTest.changeScene(Scenes.Gone, "")
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartableTest.kt
new file mode 100644
index 0000000..695edaf
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartableTest.kt
@@ -0,0 +1,248 @@
+/*
+ * 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.scene.domain.startable
+
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.internal.policy.IKeyguardStateCallback
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeTrustRepository
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
+class KeyguardStateCallbackStartableTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val underTest = kosmos.keyguardStateCallbackStartable
+
+ @Test
+ fun addCallback_hydratesAllWithCurrentState() =
+ testScope.runTest {
+ val testState = setUpTest()
+ val callback = mockCallback()
+
+ underTest.addCallback(callback)
+ runCurrent()
+
+ with(testState) {
+ val captor = argumentCaptor<Boolean>()
+ verify(callback, atLeastOnce()).onShowingStateChanged(captor.capture(), eq(userId))
+ assertThat(captor.lastValue).isEqualTo(isKeyguardShowing)
+ verify(callback, atLeastOnce()).onInputRestrictedStateChanged(captor.capture())
+ assertThat(captor.lastValue).isEqualTo(isInputRestricted)
+ verify(callback, atLeastOnce()).onSimSecureStateChanged(captor.capture())
+ assertThat(captor.lastValue).isEqualTo(isSimSecure)
+ verify(callback, atLeastOnce()).onTrustedChanged(captor.capture())
+ assertThat(captor.lastValue).isEqualTo(isTrusted)
+ }
+ }
+
+ @Test
+ fun hydrateKeyguardShowingState() =
+ testScope.runTest {
+ setUpTest(isKeyguardShowing = true)
+ val callback = mockCallback()
+ underTest.addCallback(callback)
+ runCurrent()
+ verify(callback, atLeastOnce()).onShowingStateChanged(eq(true), anyInt())
+
+ unlockDevice()
+ runCurrent()
+
+ verify(callback).onShowingStateChanged(eq(false), anyInt())
+ }
+
+ @Test
+ fun hydrateInputRestrictedState() =
+ testScope.runTest {
+ setUpTest(isKeyguardShowing = true)
+ val callback = mockCallback()
+ underTest.addCallback(callback)
+ runCurrent()
+ val captor = argumentCaptor<Boolean>()
+ verify(callback, atLeastOnce()).onInputRestrictedStateChanged(captor.capture())
+ assertThat(captor.lastValue).isTrue()
+
+ unlockDevice()
+ runCurrent()
+
+ verify(callback, atLeastOnce()).onInputRestrictedStateChanged(captor.capture())
+ assertThat(captor.lastValue).isFalse()
+ }
+
+ @Test
+ fun hydrateSimSecureState() =
+ testScope.runTest {
+ setUpTest(isSimSecure = false)
+ val callback = mockCallback()
+ underTest.addCallback(callback)
+ runCurrent()
+ val captor = argumentCaptor<Boolean>()
+ verify(callback, atLeastOnce()).onSimSecureStateChanged(captor.capture())
+ assertThat(captor.lastValue).isFalse()
+
+ kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
+ runCurrent()
+
+ verify(callback, atLeastOnce()).onSimSecureStateChanged(captor.capture())
+ assertThat(captor.lastValue).isTrue()
+ }
+
+ @Test
+ fun notifyWhenKeyguardShowingChanged() =
+ testScope.runTest {
+ setUpTest(isKeyguardShowing = true)
+ val callback = mockCallback()
+ underTest.addCallback(callback)
+ runCurrent()
+ assertThat(kosmos.fakeTrustRepository.keyguardShowingChangeEventCount).isEqualTo(1)
+
+ unlockDevice()
+ runCurrent()
+
+ assertThat(kosmos.fakeTrustRepository.keyguardShowingChangeEventCount).isEqualTo(2)
+ }
+
+ @Test
+ fun notifyWhenTrustChanged() =
+ testScope.runTest {
+ setUpTest(isTrusted = false)
+ val callback = mockCallback()
+ underTest.addCallback(callback)
+ runCurrent()
+ val captor = argumentCaptor<Boolean>()
+ verify(callback, atLeastOnce()).onTrustedChanged(captor.capture())
+ assertThat(captor.lastValue).isFalse()
+
+ kosmos.fakeTrustRepository.setCurrentUserTrusted(true)
+ runCurrent()
+
+ verify(callback, atLeastOnce()).onTrustedChanged(captor.capture())
+ assertThat(captor.lastValue).isTrue()
+ }
+
+ private suspend fun TestScope.setUpTest(
+ isKeyguardShowing: Boolean = true,
+ userId: Int = selectedUser.id,
+ isInputRestricted: Boolean = true,
+ isSimSecure: Boolean = false,
+ isTrusted: Boolean = false,
+ ): TestState {
+ val testState =
+ TestState(
+ isKeyguardShowing = isKeyguardShowing,
+ userId = userId,
+ isInputRestricted = isInputRestricted,
+ isSimSecure = isSimSecure,
+ isTrusted = isTrusted,
+ )
+
+ if (isKeyguardShowing) {
+ lockDevice()
+ } else {
+ unlockDevice()
+ }
+
+ kosmos.fakeUserRepository.setUserInfos(listOf(selectedUser))
+ kosmos.fakeUserRepository.setSelectedUserInfo(selectedUser)
+
+ if (isInputRestricted && !isKeyguardShowing) {
+ // TODO(b/348644111): add support for mNeedToReshowWhenReenabled
+ } else if (!isInputRestricted) {
+ assertWithMessage(
+ "If isInputRestricted is false, isKeyguardShowing must also be false!"
+ )
+ .that(isKeyguardShowing)
+ .isFalse()
+ }
+
+ kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = isSimSecure
+
+ kosmos.fakeTrustRepository.setCurrentUserTrusted(isTrusted)
+
+ runCurrent()
+
+ underTest.start()
+
+ return testState
+ }
+
+ private fun lockDevice() {
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "")
+ }
+
+ private fun unlockDevice() {
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ }
+
+ private fun mockCallback(): IKeyguardStateCallback {
+ return mock()
+ }
+
+ private data class TestState(
+ val isKeyguardShowing: Boolean,
+ val userId: Int,
+ val isInputRestricted: Boolean,
+ val isSimSecure: Boolean,
+ val isTrusted: Boolean,
+ )
+
+ companion object {
+ private val selectedUser =
+ UserInfo(
+ /* id= */ 100,
+ /* name= */ "First user",
+ /* flags= */ 0,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index e40c8ee..fd1b213 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -26,22 +26,35 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.uiEventLoggerFake
+import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.falsingCollector
import com.android.systemui.classifier.falsingManager
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeTrustRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscreenSceneTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
@@ -51,7 +64,7 @@
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.scene.domain.interactor.sceneContainerStartable
+import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
@@ -384,6 +397,64 @@
}
@Test
+ fun switchToAOD_whenAvailable_whenDeviceSleepsLocked() =
+ testScope.runTest {
+ kosmos.lockscreenSceneTransitionInteractor.start()
+ val asleepState by
+ collectLastValue(kosmos.keyguardTransitionInteractor.asleepKeyguardState)
+ val currentTransitionInfo by
+ collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
+ val transitionState =
+ prepareState(
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Shade,
+ )
+ kosmos.keyguardRepository.setAodAvailable(true)
+ runCurrent()
+ assertThat(asleepState).isEqualTo(KeyguardState.AOD)
+ underTest.start()
+ powerInteractor.setAsleepForTest()
+ runCurrent()
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Shade,
+ toScene = Scenes.Lockscreen,
+ currentScene = flowOf(Scenes.Lockscreen),
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+
+ assertThat(currentTransitionInfo?.to).isEqualTo(KeyguardState.AOD)
+ }
+
+ @Test
+ fun switchToDozing_whenAodUnavailable_whenDeviceSleepsLocked() =
+ testScope.runTest {
+ kosmos.lockscreenSceneTransitionInteractor.start()
+ val asleepState by
+ collectLastValue(kosmos.keyguardTransitionInteractor.asleepKeyguardState)
+ val currentTransitionInfo by
+ collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
+ val transitionState =
+ prepareState(
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Shade,
+ )
+ kosmos.keyguardRepository.setAodAvailable(false)
+ runCurrent()
+ assertThat(asleepState).isEqualTo(KeyguardState.DOZING)
+ underTest.start()
+ powerInteractor.setAsleepForTest()
+ runCurrent()
+ transitionState.value = Transition(from = Scenes.Shade, to = Scenes.Lockscreen)
+ runCurrent()
+
+ assertThat(currentTransitionInfo?.to).isEqualTo(KeyguardState.DOZING)
+ }
+
+ @Test
fun switchToGoneWhenDoubleTapPowerGestureIsTriggeredFromGone() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -402,15 +473,7 @@
lastSleepReason = WakeSleepReason.POWER_BUTTON,
powerButtonLaunchGestureTriggered = false,
)
- transitionStateFlow.value =
- ObservableTransitionState.Transition(
- fromScene = Scenes.Gone,
- toScene = Scenes.Lockscreen,
- currentScene = flowOf(Scenes.Lockscreen),
- progress = flowOf(0.5f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
- )
+ transitionStateFlow.value = Transition(from = Scenes.Shade, to = Scenes.Lockscreen)
assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
kosmos.fakePowerRepository.updateWakefulness(
@@ -484,6 +547,11 @@
QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
)
.isFalse()
+ assertThat(
+ sysUiState.flags and
+ QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED != 0L
+ )
+ .isFalse()
kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(false)
runCurrent()
@@ -497,6 +565,11 @@
QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
)
.isTrue()
+ assertThat(
+ sysUiState.flags and
+ QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED != 0L
+ )
+ .isTrue()
}
@Test
@@ -1340,6 +1413,152 @@
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
}
+ @Test
+ fun switchToGone_whenKeyguardBecomesDisabled() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun switchToGone_whenKeyguardBecomesDisabled_whenOnShadeScene() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(
+ initialSceneKey = Scenes.Shade,
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ underTest.start()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun doesNotSwitchToGone_whenKeyguardBecomesDisabled_whenInLockdownMode() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+
+ kosmos.fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ kosmos.fakeBiometricSettingsRepository.setIsUserInLockdown(true)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun doesNotSwitchToGone_whenKeyguardBecomesDisabled_whenDeviceEntered() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState(
+ isDeviceUnlocked = true,
+ initialSceneKey = Scenes.Gone,
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
+ underTest.start()
+ sceneInteractor.changeScene(Scenes.Shade, "")
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isTrue()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ }
+
+ @Test
+ fun switchToLockscreen_whenKeyguardBecomesEnabled_afterHidingWhenDisabled() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun doesNotSwitchToLockscreen_whenKeyguardBecomesEnabled_ifAuthMethodBecameInsecure() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ underTest.start()
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.None
+ )
+ runCurrent()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ fun notifyKeyguardDismissCallbacks_whenUnlocking_onDismissSucceeded() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ underTest.start()
+ val dismissCallback: IKeyguardDismissCallback = mock()
+ kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+
+ // Switch to bouncer and unlock device:
+ sceneInteractor.changeScene(Scenes.Bouncer, "")
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(dismissCallback).onDismissSucceeded()
+ }
+
+ @Test
+ fun notifyKeyguardDismissCallbacks_whenLeavingBouncer_onDismissCancelled() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ prepareState()
+ underTest.start()
+ val dismissCallback: IKeyguardDismissCallback = mock()
+ kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+
+ // Switch to bouncer:
+ sceneInteractor.changeScene(Scenes.Bouncer, "")
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+
+ // Return to lockscreen:
+ sceneInteractor.changeScene(Scenes.Lockscreen, "")
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ runCurrent()
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(dismissCallback).onDismissCancelled()
+ }
+
private fun TestScope.emulateSceneTransition(
transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
toScene: SceneKey,
@@ -1382,15 +1601,10 @@
isDeviceProvisioned: Boolean = true,
isInteractive: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
- if (authenticationMethod?.isSecure == true) {
- assert(isLockscreenEnabled) {
- "Lockscreen cannot be disabled while having a secure authentication method"
- }
- if (isDeviceUnlocked) {
- kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
- }
+ if (isDeviceUnlocked) {
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
}
check(initialSceneKey != Scenes.Gone || isDeviceUnlocked) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/StatusBarStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/StatusBarStartableTest.kt
new file mode 100644
index 0000000..9601f20
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/StatusBarStartableTest.kt
@@ -0,0 +1,365 @@
+/*
+ * 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.scene.domain.startable
+
+import android.app.StatusBarManager
+import android.provider.DeviceConfig
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.internal.statusbar.statusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.navigationbar.navigationModeController
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+import com.android.systemui.testKosmos
+import com.android.systemui.util.fakeDeviceConfigProxy
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlin.reflect.full.memberProperties
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@EnableSceneContainer
+class StatusBarStartableTest : SysuiTestCase() {
+
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun testSpecs(): List<TestSpec> {
+ return listOf(
+ TestSpec(
+ id = 0,
+ expectedFlags = StatusBarManager.DISABLE_NONE,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = false,
+ isPowerGestureIntercepted = false,
+ ),
+ ),
+ TestSpec(
+ id = 1,
+ expectedFlags = StatusBarManager.DISABLE_NONE,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = true,
+ isOccluded = true,
+ isPowerGestureIntercepted = false,
+ ),
+ ),
+ TestSpec(
+ id = 2,
+ expectedFlags = StatusBarManager.DISABLE_NONE,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = false,
+ isPowerGestureIntercepted = true,
+ isOccluded = false,
+ ),
+ ),
+ TestSpec(
+ id = 3,
+ expectedFlags = StatusBarManager.DISABLE_NONE,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = true,
+ isOccluded = true,
+ isPowerGestureIntercepted = true,
+ isAuthenticationMethodSecure = false,
+ ),
+ ),
+ TestSpec(
+ id = 4,
+ expectedFlags = StatusBarManager.DISABLE_NONE,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = true,
+ isOccluded = true,
+ isPowerGestureIntercepted = true,
+ isAuthenticationMethodSecure = true,
+ isFaceEnrolledAndEnabled = false,
+ ),
+ ),
+ TestSpec(
+ id = 5,
+ expectedFlags = StatusBarManager.DISABLE_RECENT,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = true,
+ isOccluded = true,
+ isPowerGestureIntercepted = true,
+ isAuthenticationMethodSecure = true,
+ isFaceEnrolledAndEnabled = true,
+ ),
+ ),
+ TestSpec(
+ id = 6,
+ expectedFlags = StatusBarManager.DISABLE_RECENT,
+ Preconditions(
+ isForceHideHomeAndRecents = true,
+ isShowHomeOverLockscreen = true,
+ isGesturalMode = true,
+ isPowerGestureIntercepted = false,
+ ),
+ ),
+ TestSpec(
+ id = 7,
+ expectedFlags = StatusBarManager.DISABLE_RECENT,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = true,
+ isOccluded = false,
+ isShowHomeOverLockscreen = true,
+ isGesturalMode = true,
+ isPowerGestureIntercepted = false,
+ ),
+ ),
+ TestSpec(
+ id = 8,
+ expectedFlags =
+ StatusBarManager.DISABLE_RECENT or StatusBarManager.DISABLE_HOME,
+ Preconditions(
+ isForceHideHomeAndRecents = true,
+ isShowHomeOverLockscreen = true,
+ isGesturalMode = false,
+ isPowerGestureIntercepted = false,
+ ),
+ ),
+ TestSpec(
+ id = 9,
+ expectedFlags =
+ StatusBarManager.DISABLE_RECENT or StatusBarManager.DISABLE_HOME,
+ Preconditions(
+ isForceHideHomeAndRecents = false,
+ isKeyguardShowing = true,
+ isOccluded = false,
+ isShowHomeOverLockscreen = false,
+ isPowerGestureIntercepted = false,
+ ),
+ ),
+ )
+ }
+
+ @BeforeClass
+ @JvmStatic
+ fun setUpClass() {
+ val seenIds = mutableSetOf<Int>()
+ testSpecs().forEach { testSpec ->
+ assertWithMessage("Duplicate TestSpec id=${testSpec.id}")
+ .that(seenIds)
+ .doesNotContain(testSpec.id)
+ seenIds.add(testSpec.id)
+ }
+ }
+ }
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val statusBarServiceMock = kosmos.statusBarService
+ private val flagsCaptor = argumentCaptor<Int>()
+
+ private val navigationModeControllerMock = kosmos.navigationModeController
+ private var currentNavigationMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ set(value) {
+ field = value
+ modeChangedListeners.forEach { listener -> listener.onNavigationModeChanged(field) }
+ }
+
+ private val modeChangedListeners = mutableListOf<NavigationModeController.ModeChangedListener>()
+
+ private val underTest = kosmos.statusBarStartable
+
+ @JvmField @Parameter(0) var testSpec: TestSpec? = null
+
+ @Before
+ fun setUp() {
+ whenever(navigationModeControllerMock.addListener(any())).thenAnswer { invocation ->
+ val listener = invocation.arguments[0] as NavigationModeController.ModeChangedListener
+ modeChangedListeners.add(listener)
+ currentNavigationMode
+ }
+
+ underTest.start()
+ }
+
+ @Test
+ fun test() =
+ testScope.runTest {
+ val preconditions = checkNotNull(testSpec).preconditions
+ preconditions.assertValid()
+
+ setUpWith(preconditions)
+
+ runCurrent()
+
+ verify(statusBarServiceMock, atLeastOnce())
+ .disableForUser(flagsCaptor.capture(), any(), any(), anyInt())
+ assertThat(flagsCaptor.lastValue).isEqualTo(checkNotNull(testSpec).expectedFlags)
+ }
+
+ /** Sets up the state to match what's specified in the given [preconditions]. */
+ private fun TestScope.setUpWith(
+ preconditions: Preconditions,
+ ) {
+ if (!preconditions.isKeyguardShowing) {
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ }
+ if (preconditions.isForceHideHomeAndRecents) {
+ whenIdle(Scenes.Bouncer)
+ } else if (preconditions.isKeyguardShowing) {
+ whenIdle(Scenes.Lockscreen)
+ } else {
+ whenIdle(Scenes.Gone)
+ }
+ runCurrent()
+
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop = preconditions.isOccluded,
+ taskInfo = if (preconditions.isOccluded) mock() else null,
+ )
+
+ kosmos.fakeDeviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+ preconditions.isShowHomeOverLockscreen.toString(),
+ /* makeDefault= */ false,
+ )
+ kosmos.fakeExecutor.runAllReady()
+
+ currentNavigationMode =
+ if (preconditions.isGesturalMode) {
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+ } else {
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ }
+
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ if (preconditions.isAuthenticationMethodSecure) {
+ AuthenticationMethodModel.Pin
+ } else {
+ AuthenticationMethodModel.None
+ }
+ )
+
+ kosmos.fakePowerRepository.updateWakefulness(
+ rawState =
+ if (preconditions.isPowerGestureIntercepted) WakefulnessState.AWAKE
+ else WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON,
+ powerButtonLaunchGestureTriggered = preconditions.isPowerGestureIntercepted,
+ )
+
+ kosmos.fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(
+ preconditions.isFaceEnrolledAndEnabled
+ )
+
+ runCurrent()
+ }
+
+ /** Sets up an idle state on the given [on] scene. */
+ private fun whenIdle(on: SceneKey) {
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(on))
+ kosmos.sceneInteractor.changeScene(on, "")
+ }
+
+ data class Preconditions(
+ val isForceHideHomeAndRecents: Boolean = false,
+ val isKeyguardShowing: Boolean = true,
+ val isOccluded: Boolean = false,
+ val isPowerGestureIntercepted: Boolean = false,
+ val isShowHomeOverLockscreen: Boolean = false,
+ val isGesturalMode: Boolean = true,
+ val isAuthenticationMethodSecure: Boolean = true,
+ val isFaceEnrolledAndEnabled: Boolean = false,
+ ) {
+ override fun toString(): String {
+ // Only include values set to true:
+ return buildString {
+ append("(")
+ append(
+ Preconditions::class
+ .memberProperties
+ .filter { it.get(this@Preconditions) == true }
+ .joinToString(", ") { "${it.name}=true" }
+ )
+ append(")")
+ }
+ }
+
+ fun assertValid() {
+ assertWithMessage(
+ "isForceHideHomeAndRecents means that the bouncer is showing so keyguard must" +
+ " be showing"
+ )
+ .that(!isForceHideHomeAndRecents || isKeyguardShowing)
+ .isTrue()
+ assertWithMessage("Cannot be occluded if the keyguard isn't showing")
+ .that(!isOccluded || isKeyguardShowing)
+ .isTrue()
+ }
+ }
+
+ data class TestSpec(
+ val id: Int,
+ val expectedFlags: Int,
+ val preconditions: Preconditions,
+ ) {
+ override fun toString(): String {
+ return "id=$id, expected=$expectedFlags, preconditions=$preconditions"
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflaterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflaterTest.kt
index 6de7f40..13b61bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflaterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflaterTest.kt
@@ -68,4 +68,27 @@
Assert.setTestThread(null)
}
+
+ @Test
+ fun inflate_frameHasPadding() {
+ Assert.setTestThread(Thread.currentThread())
+
+ val (frame, _) =
+ BrightnessMirrorInflater.inflate(
+ themedContext,
+ kosmos.brightnessSliderControllerFactory,
+ )
+
+ assertThat(frame.visibility).isEqualTo(View.VISIBLE)
+
+ val padding =
+ context.resources.getDimensionPixelSize(R.dimen.rounded_slider_background_padding)
+
+ assertThat(frame.paddingLeft).isEqualTo(padding)
+ assertThat(frame.paddingTop).isEqualTo(padding)
+ assertThat(frame.paddingRight).isEqualTo(padding)
+ assertThat(frame.paddingBottom).isEqualTo(padding)
+
+ Assert.setTestThread(null)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/viewmodel/BrightnessMirrorViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/viewmodel/BrightnessMirrorViewModelTest.kt
index 09fc6f9..90c11d4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/viewmodel/BrightnessMirrorViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/settings/brightness/ui/viewmodel/BrightnessMirrorViewModelTest.kt
@@ -16,10 +16,9 @@
package com.android.systemui.settings.brightness.ui.viewmodel
-import android.content.applicationContext
import android.content.res.mainResources
-import android.view.ContextThemeWrapper
import android.view.View
+import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -27,12 +26,10 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
-import com.android.systemui.settings.brightness.ui.binder.BrightnessMirrorInflater
import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
import com.android.systemui.settings.brightness.ui.viewModel.LocationAndSize
import com.android.systemui.settings.brightnessSliderControllerFactory
import com.android.systemui.testKosmos
-import com.android.systemui.util.Assert
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -47,9 +44,6 @@
private val kosmos = testKosmos()
- private val themedContext =
- ContextThemeWrapper(kosmos.applicationContext, R.style.Theme_SystemUI_QuickSettings)
-
private val underTest =
with(kosmos) {
BrightnessMirrorViewModel(
@@ -76,7 +70,7 @@
}
@Test
- fun setLocationInWindow_correctLocationAndSize() =
+ fun locationInWindowAndContainer_correctLocationAndSize() =
with(kosmos) {
testScope.runTest {
val locationAndSize by collectLastValue(underTest.locationAndSize)
@@ -101,6 +95,7 @@
whenever(measuredHeight).thenReturn(height)
whenever(measuredWidth).thenReturn(width)
}
+ val yOffsetFromContainer = setContainerViewHierarchy(mockView)
underTest.setLocationAndSize(mockView)
@@ -108,7 +103,8 @@
.isEqualTo(
// Adjust for padding around the view
LocationAndSize(
- yOffset = y - padding,
+ yOffsetFromWindow = y - padding,
+ yOffsetFromContainer = yOffsetFromContainer - padding,
width = width + 2 * padding,
height = height + 2 * padding,
)
@@ -116,31 +112,20 @@
}
}
- @Test
- fun setLocationInWindow_paddingSetToRootView() =
- with(kosmos) {
- Assert.setTestThread(Thread.currentThread())
- val padding =
- mainResources.getDimensionPixelSize(R.dimen.rounded_slider_background_padding)
+ private fun setContainerViewHierarchy(mockView: View): Int {
+ val rootView = FrameLayout(context)
+ val containerView = FrameLayout(context).apply { id = R.id.quick_settings_container }
+ val otherView = FrameLayout(context)
- val view = mock<View>()
+ rootView.addView(containerView)
+ containerView.addView(otherView)
+ otherView.addView(mockView)
- val (_, sliderController) =
- BrightnessMirrorInflater.inflate(
- themedContext,
- brightnessSliderControllerFactory,
- )
- underTest.setToggleSlider(sliderController)
+ containerView.setLeftTopRightBottom(1, /* top= */ 1, 1, 1)
+ otherView.setLeftTopRightBottom(0, /* top= */ 2, 0, 0)
+ whenever(mockView.parent).thenReturn(otherView)
+ whenever(mockView.top).thenReturn(3)
- underTest.setLocationAndSize(view)
-
- with(sliderController.rootView) {
- assertThat(paddingBottom).isEqualTo(padding)
- assertThat(paddingTop).isEqualTo(padding)
- assertThat(paddingLeft).isEqualTo(padding)
- assertThat(paddingRight).isEqualTo(padding)
- }
-
- Assert.setTestThread(null)
- }
+ return 2 + 3
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index c53cdf8..673d5ef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -32,31 +32,24 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
-import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.qs.footerActionsController
-import com.android.systemui.qs.footerActionsViewModelFactory
-import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
+import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
+import com.android.systemui.qs.ui.adapter.qsSceneAdapter
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.domain.startable.shadeStartable
import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
-import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -65,11 +58,8 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -82,32 +72,9 @@
private val testScope = kosmos.testScope
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val shadeRepository by lazy { kosmos.shadeRepository }
+ private val qsSceneAdapter by lazy { kosmos.fakeQSSceneAdapter }
- private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
-
- private lateinit var underTest: ShadeSceneViewModel
-
- @Mock private lateinit var mediaDataManager: MediaDataManager
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- underTest =
- ShadeSceneViewModel(
- applicationScope = testScope.backgroundScope,
- shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
- qsSceneAdapter = qsSceneAdapter,
- notifications = kosmos.notificationsPlaceholderViewModel,
- brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel,
- mediaCarouselInteractor = kosmos.mediaCarouselInteractor,
- shadeInteractor = kosmos.shadeInteractor,
- footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
- footerActionsController = kosmos.footerActionsController,
- sceneInteractor = kosmos.sceneInteractor,
- unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor,
- )
- }
+ private val underTest: ShadeSceneViewModel by lazy { kosmos.shadeSceneViewModel }
@Test
fun upTransitionSceneKey_deviceLocked_lockScreen() =
@@ -139,6 +106,21 @@
}
@Test
+ fun upTransitionSceneKey_keyguardDisabled_gone() =
+ testScope.runTest {
+ val destinationScenes by collectLastValue(underTest.destinationScenes)
+ val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+ assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
+ .isEqualTo(SceneFamilies.Home)
+ assertThat(homeScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
val destinationScenes by collectLastValue(underTest.destinationScenes)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index f06e04b..e73cae2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -40,9 +40,11 @@
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.mock
@@ -105,6 +107,7 @@
{ kosmos.shadeInteractor },
{ kosmos.deviceUnlockedInteractor },
{ kosmos.sceneInteractor },
+ { kosmos.sceneContainerOcclusionInteractor },
{ kosmos.keyguardClockInteractor },
) {
override fun createDarkAnimator(): ObjectAnimator {
@@ -336,6 +339,47 @@
}
@Test
+ @EnableSceneContainer
+ fun start_hydratesStatusBarState_whileOccluded() =
+ testScope.runTest {
+ var statusBarState = underTest.state
+ val listener =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ statusBarState = newState
+ }
+ }
+ underTest.addCallback(listener)
+
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ val isOccluded by
+ collectLastValue(kosmos.sceneContainerOcclusionInteractor.invisibleDueToOcclusion)
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+ showWhenLockedActivityOnTop = true,
+ taskInfo = mock(),
+ )
+ runCurrent()
+ assertThat(isOccluded).isTrue()
+
+ // Call start to begin hydrating based on the scene framework:
+ underTest.start()
+
+ kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = Scenes.QuickSettings,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+ }
+
+ @Test
fun leaveOpenOnKeyguard_whenGone_isFalse() =
testScope.runTest {
underTest.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index cc3fdc5..1f47542 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.statusbar.notification
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
@@ -48,6 +49,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@RunWithLooper
@EnableSceneContainer
class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
index 8e765f7..9ef42c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
@@ -21,13 +21,13 @@
import android.provider.Settings.Global
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.statusbar.notification.data.model.ZenMode
import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
@@ -52,24 +52,20 @@
@Before
fun setup() {
- with(kosmos) {
- underTest = NotificationsSoundPolicyInteractor(notificationsSoundPolicyRepository)
- }
+ with(kosmos) { underTest = NotificationsSoundPolicyInteractor(zenModeRepository) }
}
@Test
fun onlyAlarmsCategory_areAlarmsAllowed_isTrue() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_OFF)
val expectedByCategory =
NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.associateWith {
it == NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
}
expectedByCategory.forEach { entry ->
- notificationsSoundPolicyRepository.updateNotificationPolicy(
- priorityCategories = entry.key
- )
+ zenModeRepository.updateNotificationPolicy(priorityCategories = entry.key)
val areAlarmsAllowed by collectLastValue(underTest.areAlarmsAllowed)
runCurrent()
@@ -84,15 +80,13 @@
fun onlyMediaCategory_areAlarmsAllowed_isTrue() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_OFF)
val expectedByCategory =
NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.associateWith {
it == NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
}
expectedByCategory.forEach { entry ->
- notificationsSoundPolicyRepository.updateNotificationPolicy(
- priorityCategories = entry.key
- )
+ zenModeRepository.updateNotificationPolicy(priorityCategories = entry.key)
val isMediaAllowed by collectLastValue(underTest.isMediaAllowed)
runCurrent()
@@ -108,7 +102,7 @@
with(kosmos) {
testScope.runTest {
for (category in NotificationManager.Policy.ALL_PRIORITY_CATEGORIES) {
- notificationsSoundPolicyRepository.updateNotificationPolicy(
+ zenModeRepository.updateNotificationPolicy(
priorityCategories = category,
state = NotificationManager.Policy.STATE_UNSET,
)
@@ -126,7 +120,7 @@
fun allCategoriesAllowed_isRingerAllowed_isTrue() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateNotificationPolicy(
+ zenModeRepository.updateNotificationPolicy(
priorityCategories =
NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
acc or value
@@ -146,7 +140,7 @@
fun noCategoriesAndBlocked_isRingerAllowed_isFalse() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateNotificationPolicy(
+ zenModeRepository.updateNotificationPolicy(
priorityCategories = 0,
state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
)
@@ -163,10 +157,8 @@
fun zenModeNoInterruptions_allStreams_muted() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateNotificationPolicy()
- notificationsSoundPolicyRepository.updateZenMode(
- ZenMode(Global.ZEN_MODE_NO_INTERRUPTIONS)
- )
+ zenModeRepository.updateNotificationPolicy()
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_NO_INTERRUPTIONS)
for (stream in AudioStream.supportedStreamTypes) {
val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
@@ -182,8 +174,8 @@
fun zenModeOff_allStreams_notMuted() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateNotificationPolicy()
- notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+ zenModeRepository.updateNotificationPolicy()
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_OFF)
for (stream in AudioStream.supportedStreamTypes) {
val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
@@ -205,8 +197,8 @@
AudioManager.STREAM_SYSTEM,
)
testScope.runTest {
- notificationsSoundPolicyRepository.updateNotificationPolicy()
- notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_ALARMS))
+ zenModeRepository.updateNotificationPolicy()
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_ALARMS)
for (stream in AudioStream.supportedStreamTypes) {
val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
@@ -222,10 +214,8 @@
fun alarms_allowed_notMuted() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateZenMode(
- ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- )
- notificationsSoundPolicyRepository.updateNotificationPolicy(
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ zenModeRepository.updateNotificationPolicy(
priorityCategories = NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
)
@@ -242,10 +232,8 @@
fun media_allowed_notMuted() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateZenMode(
- ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- )
- notificationsSoundPolicyRepository.updateNotificationPolicy(
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ zenModeRepository.updateNotificationPolicy(
priorityCategories = NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
)
@@ -262,10 +250,8 @@
fun ringer_allowed_notificationsNotMuted() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateZenMode(
- ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- )
- notificationsSoundPolicyRepository.updateNotificationPolicy(
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ zenModeRepository.updateNotificationPolicy(
priorityCategories =
NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
acc or value
@@ -288,10 +274,8 @@
fun ringer_allowed_ringNotMuted() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateZenMode(
- ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- )
- notificationsSoundPolicyRepository.updateNotificationPolicy(
+ zenModeRepository.updateZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ zenModeRepository.updateNotificationPolicy(
priorityCategories =
NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
acc or value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelTest.kt
new file mode 100644
index 0000000..5e87f46
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelTest.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.app.PendingIntent
+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.IconModel
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
+import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
+import com.android.systemui.statusbar.notification.row.shared.TimerContentModel.TimerState.Paused
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+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 TimerViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val repository = kosmos.fakeNotificationRowRepository
+
+ private var contentModel: TimerContentModel?
+ get() = repository.richOngoingContentModel.value as? TimerContentModel
+ set(value) {
+ repository.richOngoingContentModel.value = value
+ }
+
+ private lateinit var underTest: TimerViewModel
+
+ @Before
+ fun setup() {
+ underTest = kosmos.getTimerViewModel(repository)
+ }
+
+ @Test
+ fun labelShowsTheTimerName() =
+ testScope.runTest {
+ val label by collectLastValue(underTest.label)
+ contentModel = pausedTimer(name = "Example Timer Name")
+ assertThat(label).isEqualTo("Example Timer Name")
+ }
+
+ @Test
+ fun pausedTimeRemainingFormatsWell() =
+ testScope.runTest {
+ val label by collectLastValue(underTest.pausedTime)
+ contentModel = pausedTimer(timeRemaining = Duration.ofMinutes(3))
+ assertThat(label).isEqualTo("3:00")
+ contentModel = pausedTimer(timeRemaining = Duration.ofSeconds(119))
+ assertThat(label).isEqualTo("1:59")
+ contentModel = pausedTimer(timeRemaining = Duration.ofSeconds(121))
+ assertThat(label).isEqualTo("2:01")
+ contentModel = pausedTimer(timeRemaining = Duration.ofHours(1))
+ assertThat(label).isEqualTo("1:00:00")
+ contentModel = pausedTimer(timeRemaining = Duration.ofHours(24))
+ assertThat(label).isEqualTo("24:00:00")
+ }
+
+ private fun pausedTimer(
+ icon: IconModel = mock(),
+ name: String = "example",
+ timeRemaining: Duration = Duration.ofMinutes(3),
+ resumeIntent: PendingIntent? = null,
+ resetIntent: PendingIntent? = null
+ ) =
+ TimerContentModel(
+ icon = icon,
+ name = name,
+ state =
+ Paused(
+ timeRemaining = timeRemaining,
+ resumeIntent = resumeIntent,
+ resetIntent = resetIntent,
+ )
+ )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 9fde116..40315a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -23,6 +23,7 @@
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import androidx.test.filters.SmallTest
+import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
@@ -56,7 +57,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.MockitoAnnotations
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -97,7 +97,9 @@
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
+ // "Why is this not lazily initialised above?" you may ask. There's a simple answer: likely
+ // due to some timing issue with how the flags are getting initialised for parameterization,
+ // some tests start failing when this isn't initialised this way. You can just leave it be.
underTest = kosmos.notificationListViewModel
}
@@ -146,36 +148,40 @@
assertThat(important).isTrue()
}
+ // NOTE: The empty shade view and the footer view should be mutually exclusive.
+
@Test
- fun shouldIncludeEmptyShadeView_trueWhenNoNotifs() =
+ fun shouldShowEmptyShadeView_trueWhenNoNotifs() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
runCurrent()
// THEN empty shade is visible
- assertThat(shouldInclude).isTrue()
+ assertThat(shouldShowEmptyShadeView).isTrue()
+ assertThat(shouldIncludeFooterView?.value).isFalse()
}
@Test
- fun shouldIncludeEmptyShadeView_falseWhenNotifs() =
+ fun shouldShowEmptyShadeView_falseWhenNotifs() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldInclude).isFalse()
+ assertThat(shouldShowEmptyShadeView).isFalse()
}
@Test
- fun shouldIncludeEmptyShadeView_falseWhenQsExpandedDefault() =
+ fun shouldShowEmptyShadeView_falseWhenQsExpandedDefault() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -184,13 +190,14 @@
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldInclude).isFalse()
+ assertThat(shouldShow).isFalse()
}
@Test
- fun shouldIncludeEmptyShadeView_trueWhenQsExpandedInSplitShade() =
+ fun shouldShowEmptyShadeView_trueWhenQsExpandedInSplitShade() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -203,13 +210,15 @@
runCurrent()
// THEN empty shade is visible
- assertThat(shouldInclude).isTrue()
+ assertThat(shouldShowEmptyShadeView).isTrue()
+ assertThat(shouldIncludeFooterView?.value).isFalse()
}
@Test
- fun shouldIncludeEmptyShadeView_trueWhenLockedShade() =
+ fun shouldShowEmptyShadeView_trueWhenLockedShade() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -218,13 +227,14 @@
runCurrent()
// THEN empty shade is visible
- assertThat(shouldInclude).isTrue()
+ assertThat(shouldShowEmptyShadeView).isTrue()
+ assertThat(shouldIncludeFooterView?.value).isFalse()
}
@Test
- fun shouldIncludeEmptyShadeView_falseWhenKeyguard() =
+ fun shouldShowEmptyShadeView_falseWhenKeyguard() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -233,13 +243,13 @@
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldInclude).isFalse()
+ assertThat(shouldShow).isFalse()
}
@Test
- fun shouldIncludeEmptyShadeView_falseWhenStartingToSleep() =
+ fun shouldShowEmptyShadeView_falseWhenStartingToSleep() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
+ val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -250,7 +260,7 @@
runCurrent()
// THEN empty shade is not visible
- assertThat(shouldInclude).isFalse()
+ assertThat(shouldShow).isFalse()
}
@Test
@@ -258,8 +268,10 @@
testScope.runTest {
val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
- zenModeRepository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
- zenModeRepository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ zenModeRepository.updateNotificationPolicy(
+ suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+ )
+ zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
runCurrent()
assertThat(hidden).isTrue()
@@ -270,8 +282,10 @@
testScope.runTest {
val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
- zenModeRepository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
- zenModeRepository.zenMode.value = Settings.Global.ZEN_MODE_OFF
+ zenModeRepository.updateNotificationPolicy(
+ suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+ )
+ zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
runCurrent()
assertThat(hidden).isFalse()
@@ -302,7 +316,8 @@
@Test
fun shouldIncludeFooterView_trueWhenShade() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -312,13 +327,15 @@
runCurrent()
// THEN footer is visible
- assertThat(shouldInclude?.value).isTrue()
+ assertThat(shouldIncludeFooterView?.value).isTrue()
+ assertThat(shouldShowEmptyShadeView).isFalse()
}
@Test
fun shouldIncludeFooterView_trueWhenLockedShade() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -328,7 +345,8 @@
runCurrent()
// THEN footer is visible
- assertThat(shouldInclude?.value).isTrue()
+ assertThat(shouldIncludeFooterView?.value).isTrue()
+ assertThat(shouldShowEmptyShadeView).isFalse()
}
@Test
@@ -404,7 +422,8 @@
@Test
fun shouldIncludeFooterView_trueWhenQsExpandedSplitShade() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -419,7 +438,8 @@
runCurrent()
// THEN footer is visible
- assertThat(shouldInclude?.value).isTrue()
+ assertThat(shouldIncludeFooterView?.value).isTrue()
+ assertThat(shouldShowEmptyShadeView).isFalse()
}
@Test
@@ -528,9 +548,7 @@
FakeHeadsUpRowRepository(key = "1"),
FakeHeadsUpRowRepository(key = "2"),
)
- headsUpRepository.setNotifications(
- rows,
- )
+ headsUpRepository.setNotifications(rows)
runCurrent()
// THEN the list is empty
@@ -566,7 +584,7 @@
headsUpRepository.setNotifications(
FakeHeadsUpRowRepository(key = "0", isPinned = true),
- FakeHeadsUpRowRepository(key = "1")
+ FakeHeadsUpRowRepository(key = "1"),
)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
index ee9fd349..4944c8b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -31,9 +32,10 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@RunWithLooper
class NotificationsPlaceholderViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val underTest = kosmos.notificationsPlaceholderViewModel
+ private val underTest by lazy { kosmos.notificationsPlaceholderViewModel }
@Test
fun onBoundsChanged() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 497484f90..f14c96ded 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -23,11 +23,15 @@
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.BrokenWithSceneContainer
@@ -134,6 +138,9 @@
val largeScreenHeaderHelper
get() = kosmos.mockLargeScreenHeaderHelper
+ val communalSceneRepository
+ get() = kosmos.communalSceneRepository
+
lateinit var underTest: SharedNotificationContainerViewModel
@Before
@@ -845,26 +852,24 @@
@Test
@DisableSceneContainer
fun updateBounds_fromGone_withoutTransitions() =
- testScope.runTest {
- // Start step is already at 1.0
- val runningStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.RUNNING)
- val finishStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.FINISHED)
+ testScope.runTest {
+ // Start step is already at 1.0
+ val runningStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.RUNNING)
+ val finishStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.FINISHED)
- val bounds by collectLastValue(underTest.bounds)
- val top = 123f
- val bottom = 456f
+ val bounds by collectLastValue(underTest.bounds)
+ val top = 123f
+ val bottom = 456f
- kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep)
- runCurrent()
- kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
- runCurrent()
- keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
- runCurrent()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep)
+ runCurrent()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
+ runCurrent()
+ keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
+ runCurrent()
- assertThat(bounds).isEqualTo(
- NotificationContainerBounds(top = top, bottom = bottom)
- )
- }
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
+ }
@Test
fun alphaOnFullQsExpansion() =
@@ -1020,6 +1025,230 @@
assertThat(fadeIn[0]).isEqualTo(false)
}
+ @Test
+ @BrokenWithSceneContainer(330311871)
+ fun alpha_isZero_fromPrimaryBouncerToGoneWhileCommunalSceneVisible() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor()
+ val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+ showPrimaryBouncer()
+ showCommunalScene()
+
+ // PRIMARY_BOUNCER->GONE transition is started
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.STARTED,
+ value = 0f,
+ )
+ )
+ runCurrent()
+
+ // PRIMARY_BOUNCER->GONE transition running
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.1f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.9f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+
+ hideCommunalScene()
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.FINISHED,
+ value = 1f
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ @BrokenWithSceneContainer(330311871)
+ fun alpha_fromPrimaryBouncerToGoneWhenCommunalSceneNotVisible() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor()
+ val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+ showPrimaryBouncer()
+ hideCommunalScene()
+
+ // PRIMARY_BOUNCER->GONE transition is started
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ // PRIMARY_BOUNCER->GONE transition running
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.1f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.9f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.FINISHED,
+ value = 1f
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ @BrokenWithSceneContainer(330311871)
+ fun alpha_isZero_fromAlternateBouncerToGoneWhileCommunalSceneVisible() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor()
+ val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+ showAlternateBouncer()
+ showCommunalScene()
+
+ // ALTERNATE_BOUNCER->GONE transition is started
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.STARTED,
+ value = 0f,
+ )
+ )
+ runCurrent()
+
+ // ALTERNATE_BOUNCER->GONE transition running
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.1f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.9f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+
+ hideCommunalScene()
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.FINISHED,
+ value = 1f
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ @BrokenWithSceneContainer(330311871)
+ fun alpha_fromAlternateBouncerToGoneWhenCommunalSceneNotVisible() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor()
+ val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+ showAlternateBouncer()
+ hideCommunalScene()
+
+ // ALTERNATE_BOUNCER->GONE transition is started
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ // ALTERNATE_BOUNCER->GONE transition running
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.1f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.RUNNING,
+ value = 0.9f,
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = GONE,
+ transitionState = TransitionState.FINISHED,
+ value = 1f
+ )
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+ }
+
private suspend fun TestScope.showLockscreen() {
shadeTestUtil.setQsExpansion(0f)
shadeTestUtil.setLockscreenShadeExpansion(0f)
@@ -1071,4 +1300,52 @@
testScope,
)
}
+
+ private suspend fun TestScope.showPrimaryBouncer() {
+ shadeTestUtil.setQsExpansion(0f)
+ shadeTestUtil.setLockscreenShadeExpansion(0f)
+ runCurrent()
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ runCurrent()
+ kosmos.keyguardBouncerRepository.setPrimaryShow(true)
+ runCurrent()
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ testScope,
+ )
+ }
+
+ private suspend fun TestScope.showAlternateBouncer() {
+ shadeTestUtil.setQsExpansion(0f)
+ shadeTestUtil.setLockscreenShadeExpansion(0f)
+ runCurrent()
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ runCurrent()
+ kosmos.keyguardBouncerRepository.setPrimaryShow(false)
+ runCurrent()
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ testScope,
+ )
+ }
+
+ private fun TestScope.showCommunalScene() {
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Communal)
+ )
+ communalSceneRepository.setTransitionState(transitionState)
+ runCurrent()
+ }
+
+ private fun TestScope.hideCommunalScene() {
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(CommunalScenes.Blank)
+ )
+ communalSceneRepository.setTransitionState(transitionState)
+ runCurrent()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index 57d3251..5887f90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -20,8 +20,11 @@
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
+import android.os.Handler
import android.os.RemoteException
import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.View
import android.widget.FrameLayout
import android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
@@ -29,6 +32,7 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.LaunchableView
@@ -36,7 +40,6 @@
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -51,26 +54,27 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.nullable
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.nullable
import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
@ExperimentalCoroutinesApi
@SmallTest
@@ -131,21 +135,22 @@
mainExecutor = mainExecutor,
communalSceneInteractor = communalSceneInteractor,
)
- whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
+ `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER)
+ `when`(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
}
@Test
fun startPendingIntentDismissingKeyguard_keyguardShowing_dismissWithAction() {
val pendingIntent = mock(PendingIntent::class.java)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
underTest.startPendingIntentDismissingKeyguard(intent = pendingIntent, dismissShade = true)
mainExecutor.runAllReady()
verify(statusBarKeyguardViewManager)
- .dismissWithAction(any(OnDismissAction::class.java), eq(null), anyBoolean(), eq(null))
+ .dismissWithAction(any(), eq(null), anyBoolean(), eq(null))
}
@Test
@@ -158,10 +163,10 @@
}
parent.addView(view)
val controller = ActivityTransitionAnimator.Controller.fromView(view)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
.thenReturn(true)
startPendingIntentMaybeDismissingKeyguard(
@@ -173,9 +178,9 @@
verify(activityTransitionAnimator)
.startPendingIntentWithAnimation(
- nullable(),
+ nullable(ActivityTransitionAnimator.Controller::class.java),
eq(true),
- nullable(),
+ nullable(String::class.java),
eq(true),
any(),
)
@@ -191,10 +196,10 @@
}
parent.addView(view)
val controller = ActivityTransitionAnimator.Controller.fromView(view)
- whenever(pendingIntent.isActivity).thenReturn(true)
- whenever(keyguardStateController.isShowing).thenReturn(true)
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- whenever(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ `when`(activityIntentHelper.wouldPendingShowOverLockscreen(eq(pendingIntent), anyInt()))
.thenReturn(false)
// extra activity options to set on pending intent
@@ -218,12 +223,12 @@
eq(context),
eq(0),
eq(fillInIntent),
- nullable(),
- nullable(),
- nullable(),
+ nullable(PendingIntent.OnFinished::class.java),
+ nullable(Handler::class.java),
+ nullable(String::class.java),
bundleCaptor.capture()
)
- val options = ActivityOptions.fromBundle(bundleCaptor.value)
+ val options = ActivityOptions.fromBundle(bundleCaptor.firstValue)
assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission).isFalse()
assertThat(options.splashScreenStyle).isEqualTo(SPLASH_SCREEN_STYLE_SOLID_COLOR)
}
@@ -243,6 +248,74 @@
verify(centralSurfaces).getAnimatorControllerFromNotification(associatedView)
}
+ @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startPendingIntentDismissingKeyguard_transitionAnimator_animateOverOcclusion() {
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ val pendingIntent = mock(PendingIntent::class.java)
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ underTest.startPendingIntentDismissingKeyguard(
+ intent = pendingIntent,
+ dismissShade = true,
+ animationController = controller,
+ showOverLockscreen = true,
+ skipLockscreenChecks = true
+ )
+ mainExecutor.runAllReady()
+
+ verify(activityTransitionAnimator)
+ .startPendingIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(true),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
+ @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startPendingIntentDismissingKeyguard_transitionAnimator_doNotAnimateOverOcclusion() {
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ val pendingIntent = mock(PendingIntent::class.java)
+ `when`(pendingIntent.isActivity).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ underTest.startPendingIntentDismissingKeyguard(
+ intent = pendingIntent,
+ dismissShade = true,
+ animationController = controller,
+ showOverLockscreen = true,
+ skipLockscreenChecks = true
+ )
+ mainExecutor.runAllReady()
+
+ verify(activityTransitionAnimator)
+ .startPendingIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(false),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
@Test
fun startActivity_noUserHandleProvided_getUserHandle() {
val intent = mock(Intent::class.java)
@@ -252,13 +325,66 @@
verify(userTracker).userHandle
}
+ @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startActivity_transitionAnimator_animateOverOcclusion() {
+ val intent = mock(Intent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ mainExecutor.runAllReady()
+ underTest.startActivity(intent, true, controller, true, null)
+
+ verify(activityTransitionAnimator)
+ .startIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(true),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
+ @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun startActivity_transitionAnimator_doNotAnimateOverOcclusion() {
+ val intent = mock(Intent::class.java)
+ val parent = FrameLayout(context)
+ val view =
+ object : View(context), LaunchableView {
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {}
+ }
+ parent.addView(view)
+ val controller = ActivityTransitionAnimator.Controller.fromView(view)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isOccluded).thenReturn(true)
+
+ mainExecutor.runAllReady()
+ underTest.startActivity(intent, true, controller, true, null)
+
+ verify(activityTransitionAnimator)
+ .startIntentWithAnimation(
+ nullable(ActivityTransitionAnimator.Controller::class.java),
+ eq(false),
+ nullable(String::class.java),
+ eq(true),
+ any(),
+ )
+ }
+
@Test
fun dismissKeyguardThenExecute_startWakeAndUnlock() {
- whenever(wakefulnessLifecycle.wakefulness)
- .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
- whenever(keyguardStateController.canDismissLockScreen()).thenReturn(true)
- whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
- whenever(dozeServiceHost.isPulsing).thenReturn(true)
+ `when`(wakefulnessLifecycle.wakefulness).thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
+ `when`(keyguardStateController.canDismissLockScreen()).thenReturn(true)
+ `when`(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
+ `when`(dozeServiceHost.isPulsing).thenReturn(true)
underTest.dismissKeyguardThenExecute({ true }, {}, false)
@@ -269,25 +395,20 @@
@Test
fun dismissKeyguardThenExecute_keyguardIsShowing_dismissWithAction() {
val customMessage = "Enter your pin."
- whenever(keyguardStateController.isShowing).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(true)
underTest.dismissKeyguardThenExecute({ true }, {}, false, customMessage)
verify(statusBarKeyguardViewManager)
- .dismissWithAction(
- any(OnDismissAction::class.java),
- any(Runnable::class.java),
- eq(false),
- eq(customMessage)
- )
+ .dismissWithAction(any(), any(), eq(false), eq(customMessage))
}
@Test
fun dismissKeyguardThenExecute_awakeDreams() {
val customMessage = "Enter your pin."
var dismissActionExecuted = false
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true)
underTest.dismissKeyguardThenExecute(
{
@@ -306,9 +427,9 @@
@Test
@Throws(RemoteException::class)
fun executeRunnableDismissingKeyguard_dreaming_notShowing_awakenDreams() {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardStateController.isOccluded).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(true)
+ `when`(keyguardStateController.isShowing).thenReturn(false)
+ `when`(keyguardStateController.isOccluded).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true)
underTest.executeRunnableDismissingKeyguard(
runnable = {},
@@ -324,9 +445,9 @@
@Test
@Throws(RemoteException::class)
fun executeRunnableDismissingKeyguard_notDreaming_notShowing_doNotAwakenDreams() {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- whenever(keyguardStateController.isOccluded).thenReturn(false)
- whenever(keyguardUpdateMonitor.isDreaming).thenReturn(false)
+ `when`(keyguardStateController.isShowing).thenReturn(false)
+ `when`(keyguardStateController.isOccluded).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isDreaming).thenReturn(false)
underTest.executeRunnableDismissingKeyguard(
runnable = {},
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
new file mode 100644
index 0000000..f1fed19
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.domain.interactor
+
+import android.app.NotificationManager.Policy
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ZenModeInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val repository = kosmos.fakeZenModeRepository
+
+ private val underTest = kosmos.zenModeInteractor
+
+ @Test
+ fun isZenModeEnabled_off() =
+ testScope.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
+ runCurrent()
+
+ assertThat(enabled).isFalse()
+ }
+
+ @Test
+ fun isZenModeEnabled_alarms() =
+ testScope.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.updateZenMode(Settings.Global.ZEN_MODE_ALARMS)
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun isZenModeEnabled_importantInterruptions() =
+ testScope.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun isZenModeEnabled_noInterruptions() =
+ testScope.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.updateZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ runCurrent()
+
+ assertThat(enabled).isTrue()
+ }
+
+ @Test
+ fun testIsZenModeEnabled_unknown() =
+ testScope.runTest {
+ val enabled by collectLastValue(underTest.isZenModeEnabled)
+
+ repository.updateZenMode(4) // this should fail if we ever add another zen mode type
+ runCurrent()
+
+ assertThat(enabled).isFalse()
+ }
+
+ @Test
+ fun areNotificationsHiddenInShade_noPolicy() =
+ testScope.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.updateNotificationPolicy(null)
+ repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun areNotificationsHiddenInShade_zenOffShadeSuppressed() =
+ testScope.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.updateNotificationPolicy(
+ suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+ )
+ repository.updateZenMode(Settings.Global.ZEN_MODE_OFF)
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun areNotificationsHiddenInShade_zenOnShadeNotSuppressed() =
+ testScope.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.updateNotificationPolicy(
+ suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_STATUS_BAR
+ )
+ repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ runCurrent()
+
+ assertThat(hidden).isFalse()
+ }
+
+ @Test
+ fun areNotificationsHiddenInShade_zenOnShadeSuppressed() =
+ testScope.runTest {
+ val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
+
+ repository.updateNotificationPolicy(
+ suppressedVisualEffects = Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST
+ )
+ repository.updateZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ runCurrent()
+
+ assertThat(hidden).isTrue()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
index d620639..6e49e43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
@@ -20,7 +20,6 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.statusbar.notification.data.model.ZenMode
import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
@@ -29,7 +28,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyRepository
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.testKosmos
import com.android.systemui.volume.data.repository.audioRepository
import com.google.common.truth.Truth.assertThat
@@ -104,10 +103,8 @@
fun zenMuted_cantChange() {
with(kosmos) {
testScope.runTest {
- notificationsSoundPolicyRepository.updateNotificationPolicy()
- notificationsSoundPolicyRepository.updateZenMode(
- ZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
- )
+ zenModeRepository.updateNotificationPolicy()
+ zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
val canChangeVolume by
collectLastValue(
@@ -141,9 +138,7 @@
with(kosmos) {
testScope.runTest {
audioRepository.setLastAudibleVolume(audioStream, 30)
- notificationsSoundPolicyRepository.updateZenMode(
- ZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
- )
+ zenModeRepository.updateZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
val model by collectLastValue(underTest.getAudioStream(audioStream))
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
index 777240c..5826b3f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.volume.panel.component.spatial
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerInteractor
+import com.android.systemui.volume.data.repository.audioRepository
import com.android.systemui.volume.domain.interactor.audioOutputInteractor
import com.android.systemui.volume.panel.component.spatial.domain.interactor.SpatialAudioComponentInteractor
@@ -27,6 +29,8 @@
SpatialAudioComponentInteractor(
audioOutputInteractor,
spatializerInteractor,
+ audioRepository,
+ backgroundCoroutineContext,
testScope.backgroundScope
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
index 2f69942..ebc78d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/SpatialAudioAvailabilityCriteriaTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.volume.panel.component.spatial.domain
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.session.MediaSession
@@ -24,12 +26,14 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LeAudioProfile
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerRepository
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.volume.localMediaController
@@ -56,8 +60,15 @@
@Before
fun setup() {
with(kosmos) {
+ val leAudioProfile =
+ mock<LeAudioProfile> {
+ whenever(profileId).thenReturn(BluetoothProfile.LE_AUDIO)
+ whenever(isEnabled(any())).thenReturn(true)
+ }
val cachedBluetoothDevice: CachedBluetoothDevice = mock {
whenever(address).thenReturn("test_address")
+ whenever(profiles).thenReturn(listOf(leAudioProfile))
+ whenever(device).thenReturn(mock<BluetoothDevice> {})
}
localMediaRepository.updateCurrentConnectedDevice(
mock<BluetoothMediaDevice> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
index c6c46fa..d5566ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
@@ -16,14 +16,22 @@
package com.android.systemui.volume.panel.component.spatial.domain.interactor
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.session.MediaSession
import android.media.session.PlaybackState
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.A2dpProfile
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.HearingAidProfile
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.flags.Flags
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -44,6 +52,7 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,15 +61,33 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class SpatialAudioComponentInteractorTest : SysuiTestCase() {
+ @get:Rule val setFlagsRule = SetFlagsRule()
private val kosmos = testKosmos()
private lateinit var underTest: SpatialAudioComponentInteractor
+ private val bluetoothDevice: BluetoothDevice = mock {}
+ private val a2dpProfile: A2dpProfile = mock {
+ whenever(profileId).thenReturn(BluetoothProfile.A2DP)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(false)
+ }
+ private val leAudioProfile: LeAudioProfile = mock {
+ whenever(profileId).thenReturn(BluetoothProfile.LE_AUDIO)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(true)
+ }
+ private val hearingAidProfile: HearingAidProfile = mock {
+ whenever(profileId).thenReturn(BluetoothProfile.HEARING_AID)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(false)
+ }
+
@Before
fun setup() {
with(kosmos) {
val cachedBluetoothDevice: CachedBluetoothDevice = mock {
whenever(address).thenReturn("test_address")
+ whenever(device).thenReturn(bluetoothDevice)
+ whenever(profiles)
+ .thenReturn(listOf(a2dpProfile, leAudioProfile, hearingAidProfile))
}
localMediaRepository.updateCurrentConnectedDevice(
mock<BluetoothMediaDevice> {
@@ -83,7 +110,7 @@
fun setEnabled_changesIsEnabled() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, true)
val values by collectValues(underTest.isEnabled)
underTest.setEnabled(SpatialAudioEnabledModel.Disabled)
@@ -106,10 +133,39 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+ fun setEnabled_determinedByBluetoothProfile_a2dpProfileEnabled() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(a2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true)
+ whenever(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(false)
+ whenever(hearingAidProfile.isEnabled(bluetoothDevice)).thenReturn(false)
+ spatializerRepository.setIsSpatialAudioAvailable(a2dpAttributes, true)
+ val values by collectValues(underTest.isEnabled)
+
+ underTest.setEnabled(SpatialAudioEnabledModel.Disabled)
+ runCurrent()
+ underTest.setEnabled(SpatialAudioEnabledModel.SpatialAudioEnabled)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ SpatialAudioEnabledModel.Unknown,
+ SpatialAudioEnabledModel.Disabled,
+ SpatialAudioEnabledModel.SpatialAudioEnabled,
+ )
+ .inOrder()
+ assertThat(spatializerRepository.getSpatialAudioCompatibleDevices())
+ .containsExactly(a2dpAttributes)
+ }
+ }
+ }
+
+ @Test
fun connectedDeviceSupports_isAvailable_SpatialAudio() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, true)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, true)
val isAvailable by collectLastValue(underTest.isAvailable)
@@ -123,8 +179,8 @@
fun connectedDeviceSupportsHeadTracking_isAvailable_HeadTracking() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, true)
- spatializerRepository.setIsHeadTrackingAvailable(headset, true)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, true)
+ spatializerRepository.setIsHeadTrackingAvailable(bleHeadsetAttributes, true)
val isAvailable by collectLastValue(underTest.isAvailable)
@@ -138,7 +194,7 @@
fun connectedDeviceDoesntSupport_isAvailable_Unavailable() {
with(kosmos) {
testScope.runTest {
- spatializerRepository.setIsSpatialAudioAvailable(headset, false)
+ spatializerRepository.setIsSpatialAudioAvailable(bleHeadsetAttributes, false)
val isAvailable by collectLastValue(underTest.isAvailable)
@@ -179,7 +235,13 @@
}
private companion object {
- val headset =
+ val a2dpAttributes =
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ "test_address"
+ )
+ val bleHeadsetAttributes =
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_HEADSET,
diff --git a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
index cf9ca15..c9850f2 100644
--- a/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
@@ -19,8 +19,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:id="@+id/alternate_bouncer"
- android:focusable="true"
- android:clickable="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 186bd7c..ba0d7de 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -175,4 +175,6 @@
<dimen name="sfps_progress_bar_padding_from_edge">7dp</dimen>
<dimen name="keyguard_presentation_width">410dp</dimen>
+
+ <dimen name="appclips_backlinks_icon_size">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res-product/values-fa/strings.xml b/packages/SystemUI/res-product/values-fa/strings.xml
index 30121cc..40f8d0d 100644
--- a/packages/SystemUI/res-product/values-fa/strings.xml
+++ b/packages/SystemUI/res-product/values-fa/strings.xml
@@ -43,18 +43,18 @@
<string name="thermal_shutdown_title" product="default" msgid="8039593017174903505">"تلفن بهعلت گرم شدن خاموش شد"</string>
<string name="thermal_shutdown_title" product="device" msgid="2954206342842856379">"دستگاه بهعلت گرم شدن خاموش شد"</string>
<string name="thermal_shutdown_title" product="tablet" msgid="8941033526856177533">"رایانه لوحی بهعلت گرم شدن خاموش شد"</string>
- <string name="thermal_shutdown_message" product="default" msgid="6685194547904051408">"اکنون عملکرد تلفنتان به حالت عادی برگشته است.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
- <string name="thermal_shutdown_message" product="device" msgid="3039675532521590478">"اکنون عملکرد دستگاهتان به حالت عادی برگشته است.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
- <string name="thermal_shutdown_message" product="tablet" msgid="5285898074484811386">"اکنون عملکرد رایانه لوحیتان به حالت عادی برگشته است.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
+ <string name="thermal_shutdown_message" product="default" msgid="6685194547904051408">"اکنون عملکرد تلفنتان به حالت عادی برگشته است.\nبرای اطلاعات بیشتر تکضرب بزنید"</string>
+ <string name="thermal_shutdown_message" product="device" msgid="3039675532521590478">"اکنون عملکرد دستگاهتان به حالت عادی برگشته است.\nبرای اطلاعات بیشتر تکضرب بزنید"</string>
+ <string name="thermal_shutdown_message" product="tablet" msgid="5285898074484811386">"اکنون عملکرد رایانه لوحیتان به حالت عادی برگشته است.\nبرای اطلاعات بیشتر تکضرب بزنید"</string>
<string name="thermal_shutdown_dialog_message" product="default" msgid="6145923570358574186">"تلفنتان خیلی گرم شده بود، بنابراین خاموش شد تا خنک شود. اکنون تلفنتان عملکرد معمولش را دارد.\n\nتلفنتان ممکن است خیلی گرم شود، اگر:\n • از برنامههای نیازمند پردازش زیاد (مثل بازی، ویدیو، یا برنامههای ناوبری) استفاده کنید\n • فایلهای بزرگ بارگیری یا بارگذاری کنید\n • در دماهای بالا از تلفنتان استفاده کنید"</string>
<string name="thermal_shutdown_dialog_message" product="device" msgid="3647879000909527365">"دستگاهتان خیلی گرم شده بود، بنابراین خاموش شد تا خنک شود. اکنون دستگاهتان عملکرد معمولش را دارد.\n\nدستگاهتان ممکن است خیلی گرم شود، اگر:\n • از برنامههای نیازمند پردازش زیاد (مثل بازی، ویدیو، یا برنامههای ناوبری) استفاده کنید\n • فایلهای بزرگ بارگیری یا بارگذاری کنید\n • در دماهای بالا از دستگاهتان استفاده کنید"</string>
<string name="thermal_shutdown_dialog_message" product="tablet" msgid="8274487811928782165">"رایانه لوحیتان خیلی گرم شده بود، بنابراین خاموش شد تا خنک شود. اکنون رایانه لوحیتان عملکرد معمولش را دارد.\n\nرایانه لوحیتان ممکن است خیلی گرم شود، اگر:\n • از برنامههای نیازمند پردازش زیاد (مثل بازی، ویدیو، یا برنامههای ناوبری) استفاده کنید\n • فایلهای بزرگ بارگیری یا بارگذاری کنید\n • در دماهای بالا از رایانه لوحیتان استفاده کنید"</string>
<string name="high_temp_title" product="default" msgid="5365000411304924115">"تلفن درحال گرم شدن است"</string>
<string name="high_temp_title" product="device" msgid="6622009907401563664">"دستگاه درحال گرم شدن است"</string>
<string name="high_temp_title" product="tablet" msgid="9039733706606446616">"رایانه لوحی درحال گرم شدن است"</string>
- <string name="high_temp_notif_message" product="default" msgid="3928947950087257452">"وقتی تلفن درحال خنک شدن است، بعضیاز ویژگیها محدود میشوند.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
- <string name="high_temp_notif_message" product="device" msgid="6105125771372547292">"وقتی دستگاه درحال خنک شدن است، بعضیاز ویژگیها محدود میشوند.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
- <string name="high_temp_notif_message" product="tablet" msgid="7799279192797476850">"وقتی رایانه لوحی درحال خنک شدن است، بعضیاز ویژگیها محدود میشوند.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
+ <string name="high_temp_notif_message" product="default" msgid="3928947950087257452">"وقتی تلفن درحال خنک شدن است، بعضیاز ویژگیها محدود میشوند.\nبرای اطلاعات بیشتر تکضرب بزنید"</string>
+ <string name="high_temp_notif_message" product="device" msgid="6105125771372547292">"وقتی دستگاه درحال خنک شدن است، بعضیاز ویژگیها محدود میشوند.\nبرای اطلاعات بیشتر تکضرب بزنید"</string>
+ <string name="high_temp_notif_message" product="tablet" msgid="7799279192797476850">"وقتی رایانه لوحی درحال خنک شدن است، بعضیاز ویژگیها محدود میشوند.\nبرای اطلاعات بیشتر تکضرب بزنید"</string>
<string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"تلفنتان بهطور خودکار سعی میکند خنک شود. همچنان میتوانید از تلفنتان استفاده کنید، اما ممکن است کندتر عمل کند.\n\nوقتی تلفن خنک شد، عملکرد عادیاش از سرگرفته میشود."</string>
<string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"دستگاهتان بهطور خودکار سعی میکند خنک شود. همچنان میتوانید از دستگاهتان استفاده کنید، اما ممکن است کندتر عمل کند.\n\nوقتی دستگاه خنک شد، عملکرد عادیاش از سرگرفته میشود."</string>
<string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"رایانه لوحیتان بهطور خودکار سعی میکند خنک شود. همچنان میتوانید از رایانه لوحیتان استفاده کنید، اما ممکن است کندتر عمل کند.\n\nوقتی رایانه لوحی خنک شد، عملکرد عادیاش از سرگرفته میشود."</string>
diff --git a/services/tests/dreamservicetests/res/xml/test_dream_metadata_nonexistent_settings.xml b/packages/SystemUI/res/drawable/backlinks_rounded_rectangle.xml
similarity index 63%
rename from services/tests/dreamservicetests/res/xml/test_dream_metadata_nonexistent_settings.xml
rename to packages/SystemUI/res/drawable/backlinks_rounded_rectangle.xml
index a19ef0c..225f7bd 100644
--- a/services/tests/dreamservicetests/res/xml/test_dream_metadata_nonexistent_settings.xml
+++ b/packages/SystemUI/res/drawable/backlinks_rounded_rectangle.xml
@@ -1,4 +1,4 @@
-<!--
+<?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");
@@ -13,8 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<!-- The settings activity does not exist, which is invalid. -->
-<dream xmlns:android="http://schemas.android.com/apk/res/android"
- android:settingsActivity="com.android.frameworks.dreamservicetests/.TestDreamSettingsActivity2"
- android:showClockAndComplications="false"/>
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetBottom="4dp"
+ android:insetTop="4dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="8dp" />
+ <solid android:color="@android:color/system_surface_container_highest_light" />
+ </shape>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_dark_to_checkmark.xml b/packages/SystemUI/res/drawable/face_dialog_dark_to_checkmark.xml
deleted file mode 100644
index fb30249..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_dark_to_checkmark.xml
+++ /dev/null
@@ -1,637 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2018 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
- -->
-
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector
- android:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G_N_2_T_0"
- android:translateX="30"
- android:translateY="30">
- <group
- android:name="_R_G_L_0_G"
- android:translateX="-30"
- android:translateY="-30">
- <group
- android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
- android:scaleX="0.08"
- android:scaleY="0.08"
- android:translateX="30.1"
- android:translateY="30.083">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="0"
- android:fillColor="@color/biometric_dialog_face_checkmark"
- android:fillType="nonZero"
- android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
- android:trimPathStart="0"
- android:trimPathEnd="0"
- android:trimPathOffset="0" />
- </group>
- <group
- android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
- android:scaleX="0.08"
- android:scaleY="0.08"
- android:translateX="30.1"
- android:translateY="30.083">
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M-116 -16.5 C-116,-16.5 -31.25,68.5 -31.25,68.5 C-31.25,68.5 108.75,-71.5 108.75,-71.5 "
- android:strokeWidth="20"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_face_checkmark"
- android:trimPathStart="0"
- android:trimPathEnd="0"
- android:trimPathOffset="0" />
- </group>
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_face_checkmark"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <group
- android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
- android:pivotX="1.05"
- android:pivotY="-9.891"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="29.044"
- android:translateY="41.647">
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:pathData=" M4.71 1.1 C3.71,2.12 2.32,2.75 0.79,2.75 C-2.25,2.75 -4.7,0.29 -4.7,-2.75 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- </group>
- <group
- android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="41.1"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="18.6"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_5_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="30.727"
- android:translateY="31.703">
- <path
- android:name="_R_G_L_0_G_D_6_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
- </group>
- </group>
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleX"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleY"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleX"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleY"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="233"
- android:propertyName="trimPathEnd"
- android:startOffset="33"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="33"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.08"
- android:valueTo="0.08"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleX"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleY"
- android:startOffset="33"
- android:valueFrom="0.08"
- android:valueTo="0.12789"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.537,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleX"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleY"
- android:startOffset="200"
- android:valueFrom="0.12789"
- android:valueTo="0.12241"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.441,0 0.533,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="300"
- android:valueFrom="0.12241"
- android:valueTo="0.125"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.424,0 0.486,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="33"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="233"
- android:propertyName="trimPathEnd"
- android:startOffset="33"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.292,0 0.155,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_accent"
- android:valueTo="@color/biometric_dialog_face_checkmark"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="67"
- android:valueFrom="@color/biometric_dialog_accent"
- android:valueTo="@color/biometric_dialog_face_checkmark"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.65"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.65"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_5_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_5_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_6_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_6_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="83"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.287,0.12 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="383"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_dark_to_error.xml b/packages/SystemUI/res/drawable/face_dialog_dark_to_error.xml
deleted file mode 100644
index 0c05019..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_dark_to_error.xml
+++ /dev/null
@@ -1,473 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2018 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
- -->
-
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector
- android:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G_N_1_T_0"
- android:translateX="30"
- android:translateY="30">
- <group
- android:name="_R_G_L_0_G"
- android:translateX="-30"
- android:translateY="-30">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <group
- android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="41.1"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="18.6"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
- android:translateX="30.3"
- android:translateY="31.45">
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c " />
- </group>
- </group>
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="50"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="50"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="strokeWidth"
- android:startOffset="0"
- android:valueFrom="2"
- android:valueTo="2.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:valueTo="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0.34"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5700000000000001"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="50"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:pathData="M 41.1,23.8C 40.547981774806985,23.08834635019302 38.34001822519301,20.24165364980698 37.788,19.53"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="50"
- android:valueFrom="1"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:pathData="M 18.6,23.8C 19.16757813692093,23.08502601385117 21.43742186307907,20.224973986148832 22.005,19.51"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="67"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.999,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="fillColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillColor"
- android:startOffset="50"
- android:valueFrom="@color/biometric_dialog_gray"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,31.82259113788605 30.3,31.45"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="150"
- android:pathData="M 30.3,31.45C 30.3,31.07740886211395 30.3,29.58759113788605 30.3,29.215"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="67">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="67"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="150"
- android:propertyName="pathData"
- android:startOffset="67"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="100"
- android:propertyName="pathData"
- android:startOffset="217"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-13.15 -1.5,-13.15 C-1.5,-13.15 0.9,-13.15 0.9,-13.15 C0.9,-13.15 0.9,3.25 0.9,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_error_to_idle.xml b/packages/SystemUI/res/drawable/face_dialog_error_to_idle.xml
deleted file mode 100644
index d3cee25..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_error_to_idle.xml
+++ /dev/null
@@ -1,509 +0,0 @@
-<!--
- ~ Copyright (C) 2019 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
- -->
-
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector
- android:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group android:name="_R_G_L_0_G">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_error"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_error"
- android:trimPathStart="0.34"
- android:trimPathEnd="0.5700000000000001"
- android:trimPathOffset="0" />
- <group
- android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
- android:scaleX="0.3"
- android:scaleY="0.3"
- android:translateX="37.788"
- android:translateY="19.53">
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.1,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0"
- android:scaleX="0.3"
- android:scaleY="0.3"
- android:translateX="22.005"
- android:translateY="19.51">
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <group
- android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0"
- android:translateX="30.3"
- android:translateY="29.215">
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_error"
- android:fillType="nonZero"
- android:pathData=" M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c " />
- </group>
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="83"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="strokeColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeColor"
- android:startOffset="83"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="strokeWidth"
- android:startOffset="0"
- android:valueFrom="2.5"
- android:valueTo="2"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M34.78 38.76 C33.83,38.75 31.54,38.75 30.01,38.75 C26.97,38.75 26.14,38.75 24.3,38.76 "
- android:valueTo="M33.75 42.75 C32.75,43.76 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathStart"
- android:startOffset="0"
- android:valueFrom="0.34"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="217"
- android:propertyName="trimPathEnd"
- android:startOffset="0"
- android:valueFrom="0.5700000000000001"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="83"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 37.235981536388394,18.81834629058838 37.788,19.53"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:pathData="M 37.788,19.53C 38.3400184636116,20.241653709411622 40.5479815363884,23.08834629058838 41.1,23.8"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="50">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleX"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleY"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:startOffset="83"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 22.57257801771164,18.79502589464188 22.005,19.51"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="167"
- android:pathData="M 22.005,19.51C 21.43742198228836,20.224974105358122 19.16757801771164,23.08502589464188 18.6,23.8"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="50">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="50"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="0.3"
- android:valueTo="0.3"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleX"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="117"
- android:propertyName="scaleY"
- android:startOffset="50"
- android:valueFrom="0.3"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="fillColor"
- android:startOffset="0"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_error"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="17"
- android:propertyName="fillColor"
- android:startOffset="83"
- android:valueFrom="@color/biometric_dialog_error"
- android:valueTo="@color/biometric_dialog_gray"
- android:valueType="colorType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="150"
- android:pathData="M 30.3,29.215C 30.3,29.58759101867676 30.3,31.077408981323238 30.3,31.45"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="83"
- android:propertyName="pathData"
- android:startOffset="0"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-11.71 -1.5,-11.71 C-1.5,-11.71 0.9,-11.71 0.9,-11.71 C0.9,-11.71 0.9,3.25 0.9,3.25c "
- android:valueTo="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.321,0 0.67,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="133"
- android:propertyName="pathData"
- android:startOffset="83"
- android:valueFrom="M0.9 3.25 C0.9,3.25 -1.5,3.25 -1.5,3.25 C-1.5,3.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,1.25 -1.5,1.25 C-1.5,1.25 -1.5,-3.25 -1.5,-3.25 C-1.5,-3.25 0.9,-3.25 0.9,-3.25 C0.9,-3.25 0.9,3.25 0.9,3.25c "
- android:valueTo="M2.6 3.25 C2.6,3.25 -2.6,3.25 -2.6,3.25 C-2.6,3.25 -2.6,1.25 -2.6,1.25 C-2.6,1.25 0.6,1.25 0.6,1.25 C0.6,1.25 0.6,-3.25 0.6,-3.25 C0.6,-3.25 2.6,-3.25 2.6,-3.25 C2.6,-3.25 2.6,3.25 2.6,3.25c "
- android:valueType="pathType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.568,0 0.456,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="267"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_idle_static.xml b/packages/SystemUI/res/drawable/face_dialog_idle_static.xml
deleted file mode 100644
index 6ad8e83..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_idle_static.xml
+++ /dev/null
@@ -1,276 +0,0 @@
-<!--
- ~ Copyright (C) 2019 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
- -->
-
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector
- android:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:scaleX="1.03"
- android:scaleY="1.03">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1.0"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1.0"
- android:strokeColor="@color/biometric_dialog_gray"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1.0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1.0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1.0"
- android:fillColor="@color/biometric_dialog_gray"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="strokeAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.118,1.266 0.419,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="fillAlpha"
- android:startOffset="500"
- android:valueFrom="1.0"
- android:valueTo="1.0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleX"
- android:startOffset="500"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.204,0.46 0.568,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="500"
- android:propertyName="scaleY"
- android:startOffset="500"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.204,0.46 0.568,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="1017"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_pulse_dark_to_light.xml b/packages/SystemUI/res/drawable/face_dialog_pulse_dark_to_light.xml
deleted file mode 100644
index 427be14..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_pulse_dark_to_light.xml
+++ /dev/null
@@ -1,183 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector
- android:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:scaleX="1"
- android:scaleY="1">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.5"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.03"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="350"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_pulse_light_to_dark.xml b/packages/SystemUI/res/drawable/face_dialog_pulse_light_to_dark.xml
deleted file mode 100644
index ab26408..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_pulse_light_to_dark.xml
+++ /dev/null
@@ -1,183 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector
- android:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:scaleX="1.03"
- android:scaleY="1.03">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="0.5"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="0.5"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="0.5"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="0.5"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M16.5 23.8 C16.5,25 17.4,25.9 18.6,25.9 C19.8,25.9 20.7,25 20.7,23.8 C20.7,22.6 19.8,21.7 18.6,21.7 C17.4,21.7 16.5,22.6 16.5,23.8c " />
- <path
- android:name="_R_G_L_0_G_D_4_P_0"
- android:fillAlpha="0.5"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_0_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_1_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="strokeAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_2_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_3_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G_D_4_P_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="fillAlpha"
- android:startOffset="0"
- android:valueFrom="0.5"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1.03"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.317,0 0.287,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="350"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/face_dialog_wink_from_dark.xml b/packages/SystemUI/res/drawable/face_dialog_wink_from_dark.xml
deleted file mode 100644
index 0cd542d..0000000
--- a/packages/SystemUI/res/drawable/face_dialog_wink_from_dark.xml
+++ /dev/null
@@ -1,208 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt">
- <aapt:attr name="android:drawable">
- <vector
- android:width="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group android:name="_R_G">
- <group android:name="_R_G_L_1_G">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
- android:strokeWidth="2.5"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- </group>
- <group
- android:name="_R_G_L_0_G"
- android:pivotX="30"
- android:pivotY="30"
- android:rotation="0"
- android:scaleX="1"
- android:scaleY="1">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:pathData=" M33.75 42.75 C32.75,43.77 31.37,44.39 29.83,44.39 C26.8,44.39 24.34,41.93 24.34,38.9 "
- android:strokeWidth="2"
- android:strokeAlpha="1"
- android:strokeColor="@color/biometric_dialog_accent"
- android:trimPathStart="0"
- android:trimPathEnd="1"
- android:trimPathOffset="0" />
- <path
- android:name="_R_G_L_0_G_D_1_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M39 23.8 C39,25 39.9,25.9 41.1,25.9 C42.2,25.9 43.2,25 43.2,23.8 C43.2,22.6 42.3,21.7 41.1,21.7 C39.9,21.7 39,22.6 39,23.8c " />
- <group
- android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0"
- android:scaleX="1"
- android:scaleY="1"
- android:translateX="18.6"
- android:translateY="23.8">
- <path
- android:name="_R_G_L_0_G_D_2_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M-2.1 0 C-2.1,1.2 -1.2,2.1 0,2.1 C1.2,2.1 2.1,1.2 2.1,0 C2.1,-1.2 1.2,-2.1 0,-2.1 C-1.2,-2.1 -2.1,-1.2 -2.1,0c " />
- </group>
- <path
- android:name="_R_G_L_0_G_D_3_P_0"
- android:fillAlpha="1"
- android:fillColor="@color/biometric_dialog_accent"
- android:fillType="nonZero"
- android:pathData=" M33.33 34.95 C33.33,34.95 28.13,34.95 28.13,34.95 C28.13,34.95 28.13,32.95 28.13,32.95 C28.13,32.95 31.33,32.95 31.33,32.95 C31.33,32.95 31.33,28.45 31.33,28.45 C31.33,28.45 33.33,28.45 33.33,28.45 C33.33,28.45 33.33,34.95 33.33,34.95c " />
- </group>
- </group>
- <group android:name="time_group" />
- </vector>
- </aapt:attr>
- <target android:name="_R_G_L_0_G_D_2_P_0_G_0_T_0">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="0.26"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="433"
- android:valueFrom="1"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleY"
- android:startOffset="433"
- android:valueFrom="0.26"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="433"
- android:propertyName="rotation"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="-14"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="rotation"
- android:startOffset="433"
- android:valueFrom="-14"
- android:valueTo="0"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.305,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="_R_G_L_0_G">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleX"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.06"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="433"
- android:propertyName="scaleY"
- android:startOffset="0"
- android:valueFrom="1"
- android:valueTo="1.06"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.2,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleX"
- android:startOffset="433"
- android:valueFrom="1.06"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.305,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- <objectAnimator
- android:duration="317"
- android:propertyName="scaleY"
- android:startOffset="433"
- android:valueFrom="1.06"
- android:valueTo="1"
- android:valueType="floatType">
- <aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.305,1 1.0,1.0" />
- </aapt:attr>
- </objectAnimator>
- </set>
- </aapt:attr>
- </target>
- <target android:name="time_group">
- <aapt:attr name="android:animation">
- <set android:ordering="together">
- <objectAnimator
- android:duration="1017"
- android:propertyName="translateX"
- android:startOffset="0"
- android:valueFrom="0"
- android:valueTo="1"
- android:valueType="floatType" />
- </set>
- </aapt:attr>
- </target>
-</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_check_box.xml b/packages/SystemUI/res/drawable/ic_check_box.xml
deleted file mode 100644
index a8d1a65..0000000
--- a/packages/SystemUI/res/drawable/ic_check_box.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- 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
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/checked"
- android:state_checked="true"
- android:drawable="@drawable/ic_check_box_blue_24dp" />
- <item
- android:id="@+id/unchecked"
- android:state_checked="false"
- android:drawable="@drawable/ic_check_box_outline_24dp" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
deleted file mode 100644
index 43cae69..0000000
--- a/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- 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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"
- android:fillColor="#4285F4"/>
-</vector>
-
diff --git a/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
deleted file mode 100644
index f6f453a..0000000
--- a/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- 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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"
- android:fillColor="#757575"/>
-</vector>
-
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
deleted file mode 100644
index ae0d562..0000000
--- a/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- 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
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
- android:fillColor="#000000"/>
- <path
- android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
- android:fillColor="#000000"/>
- <path
- android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
- android:fillColor="#000000"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/placeholder_touchpad_back_gesture.png b/packages/SystemUI/res/drawable/placeholder_touchpad_back_gesture.png
new file mode 100644
index 0000000..526b585
--- /dev/null
+++ b/packages/SystemUI/res/drawable/placeholder_touchpad_back_gesture.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/placeholder_touchpad_tablet_back_gesture.png b/packages/SystemUI/res/drawable/placeholder_touchpad_tablet_back_gesture.png
new file mode 100644
index 0000000..cba2d20
--- /dev/null
+++ b/packages/SystemUI/res/drawable/placeholder_touchpad_tablet_back_gesture.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index bcc7bca..6d4e410 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -51,6 +51,32 @@
app:layout_constraintStart_toEndOf="@id/save"
app:layout_constraintTop_toTopOf="parent" />
+ <CheckBox
+ android:id="@+id/backlinks_include_data"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="16dp"
+ android:checked="true"
+ android:text="@string/backlinks_include_link"
+ android:visibility="gone"
+ app:layout_constraintBottom_toTopOf="@id/preview"
+ app:layout_constraintStart_toEndOf="@id/cancel"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <TextView
+ android:id="@+id/backlinks_data"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="16dp"
+ android:background="@drawable/backlinks_rounded_rectangle"
+ android:drawablePadding="4dp"
+ android:gravity="center"
+ android:paddingHorizontal="8dp"
+ android:visibility="gone"
+ app:layout_constraintBottom_toTopOf="@id/preview"
+ app:layout_constraintStart_toEndOf="@id/backlinks_include_data"
+ app:layout_constraintTop_toTopOf="parent" />
+
<ImageView
android:id="@+id/preview"
android:layout_width="0px"
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
deleted file mode 100644
index 5b1ec7f..0000000
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ /dev/null
@@ -1,146 +0,0 @@
-<?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.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/device_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginBottom="12dp">
- <FrameLayout
- android:id="@+id/item_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/media_output_item_background"
- android:layout_gravity="center_vertical|start">
- <com.android.systemui.media.dialog.MediaOutputSeekbar
- android:id="@+id/volume_seekbar"
- android:splitTrack="false"
- android:visibility="gone"
- android:paddingStart="0dp"
- android:paddingEnd="0dp"
- android:background="@null"
- android:contentDescription="@string/media_output_dialog_accessibility_seekbar"
- android:progressDrawable="@drawable/media_output_dialog_seekbar_background"
- android:thumb="@null"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- </FrameLayout>
-
- <FrameLayout
- android:layout_width="56dp"
- android:layout_height="64dp"
- android:layout_gravity="center_vertical|start">
- <ImageView
- android:id="@+id/title_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="center"/>
- </FrameLayout>
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="56dp"
- android:layout_marginEnd="56dp"
- android:ellipsize="end"
- android:maxLines="1"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textSize="16sp"/>
-
- <LinearLayout
- android:id="@+id/two_line_layout"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:layout_height="48dp"
- android:layout_marginEnd="56dp"
- android:layout_marginStart="56dp">
- <TextView
- android:id="@+id/two_line_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textColor="@color/media_dialog_item_main_content"
- android:textSize="16sp"/>
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="@color/media_dialog_item_main_content"
- android:textSize="14sp"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:visibility="gone"/>
- </LinearLayout>
-
- <ProgressBar
- android:id="@+id/volume_indeterminate_progress"
- style="?android:attr/progressBarStyleSmallTitle"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginEnd="16dp"
- android:indeterminate="true"
- android:layout_gravity="end|center"
- android:indeterminateOnly="true"
- android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/media_output_item_status"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginEnd="16dp"
- android:indeterminate="true"
- android:layout_gravity="end|center"
- android:indeterminateOnly="true"
- android:importantForAccessibility="no"
- android:visibility="gone"/>
-
- <LinearLayout
- android:id="@+id/end_action_area"
- android:visibility="gone"
- android:orientation="vertical"
- android:layout_width="48dp"
- android:layout_height="64dp"
- android:layout_gravity="end|center"
- android:gravity="center_vertical">
- <CheckBox
- android:id="@+id/check_box"
- android:focusable="false"
- android:importantForAccessibility="no"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginEnd="16dp"
- android:layout_gravity="end"
- android:button="@drawable/ic_circle_check_box"
- android:visibility="gone"
- />
-
- </LinearLayout>
- </FrameLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index cd5c37d..154397d 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -34,18 +34,20 @@
android:background="@drawable/ongoing_activity_chip_bg"
android:paddingStart="@dimen/ongoing_activity_chip_side_padding"
android:paddingEnd="@dimen/ongoing_activity_chip_side_padding"
- android:contentDescription="@string/ongoing_phone_call_content_description"
android:minWidth="@dimen/min_clickable_item_size"
>
<ImageView
android:src="@*android:drawable/ic_phone"
android:id="@+id/ongoing_activity_chip_icon"
+ android:contentDescription="@string/ongoing_phone_call_content_description"
android:layout_width="@dimen/ongoing_activity_chip_icon_size"
android:layout_height="@dimen/ongoing_activity_chip_icon_size"
android:tint="?android:attr/colorPrimary"
/>
+ <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever
+ be shown at one time. -->
<com.android.systemui.statusbar.chips.ui.view.ChipChronometer
android:id="@+id/ongoing_activity_chip_time"
android:layout_width="wrap_content"
@@ -58,5 +60,19 @@
android:textColor="?android:attr/colorPrimary"
/>
+ <!-- Used to show generic text in the chip instead of a timer. -->
+ <TextView
+ android:id="@+id/ongoing_activity_chip_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center|start"
+ android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
+ android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textColor="?android:attr/colorPrimary"
+ android:visibility="gone"
+ />
+
</com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/rich_ongoing_timer_notification.xml b/packages/SystemUI/res/layout/rich_ongoing_timer_notification.xml
new file mode 100644
index 0000000..f2bfbe5c9
--- /dev/null
+++ b/packages/SystemUI/res/layout/rich_ongoing_timer_notification.xml
@@ -0,0 +1,116 @@
+<?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.TimerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/topBaseline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_begin="22sp"
+ />
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:src="@drawable/ic_close"
+ app:tint="@android:color/white"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/label"
+ android:baseline="18dp"
+ app:layout_constraintBaseline_toTopOf="@id/topBaseline"
+ />
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toEndOf="@id/icon"
+ app:layout_constraintEnd_toStartOf="@id/chronoRemaining"
+ android:singleLine="true"
+ tools:text="15s Timer"
+ app:layout_constraintBaseline_toTopOf="@id/topBaseline"
+ android:paddingEnd="4dp"
+ />
+ <Chronometer
+ android:id="@+id/chronoRemaining"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textSize="20sp"
+ android:gravity="end"
+ tools:text="0:12"
+ app:layout_constraintBaseline_toTopOf="@id/topBaseline"
+ app:layout_constraintEnd_toStartOf="@id/pausedTimeRemaining"
+ app:layout_constraintStart_toEndOf="@id/label"
+ android:countDown="true"
+ android:paddingEnd="4dp"
+ />
+ <TextView
+ android:id="@+id/pausedTimeRemaining"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textSize="20sp"
+ android:gravity="end"
+ tools:text="0:12"
+ app:layout_constraintBaseline_toTopOf="@id/topBaseline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/chronoRemaining"
+ android:paddingEnd="4dp"
+ />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/bottomOfTop"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:barrierDirection="bottom"
+ app:constraint_referenced_ids="icon,label,chronoRemaining,pausedTimeRemaining"
+ />
+
+ <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
+ android:id="@+id/mainButton"
+ android:layout_width="124dp"
+ android:layout_height="wrap_content"
+ tools:text="Reset"
+ tools:drawableStart="@android:drawable/ic_menu_add"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/altButton"
+ app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
+ app:layout_constraintHorizontal_chainStyle="spread"
+ android:paddingEnd="4dp"
+ />
+
+ <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
+ android:id="@+id/altButton"
+ tools:text="Reset"
+ tools:drawableStart="@android:drawable/ic_menu_add"
+ android:drawablePadding="2dp"
+ android:drawableTint="@android:color/white"
+ android:layout_width="124dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
+ app:layout_constraintStart_toEndOf="@id/mainButton"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:paddingEnd="4dp"
+ />
+</com.android.systemui.statusbar.notification.row.ui.view.TimerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index 84ab0f1..fff1de7 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -51,7 +51,6 @@
android:layout_marginBottom="@dimen/overlay_border_width"
android:layout_gravity="center"
android:elevation="4dp"
- android:contentDescription="@string/screenshot_edit_description"
android:scaleType="fitEnd"
android:background="@drawable/overlay_preview_background"
android:adjustViewBounds="true"
@@ -67,7 +66,6 @@
android:layout_marginBottom="@dimen/overlay_border_width"
android:layout_gravity="center"
android:elevation="4dp"
- android:contentDescription="@string/screenshot_edit_description"
android:scaleType="fitEnd"
android:background="@drawable/overlay_preview_background"
android:adjustViewBounds="true"
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index fc4bf8a..e80ed26 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -22,5 +22,4 @@
android:layout_height="wrap_content"
app:lottie_autoPlay="true"
app:lottie_loop="true"
- app:lottie_rawRes="@raw/sfps_pulse"
- android:importantForAccessibility="no"/>
\ No newline at end of file
+ app:lottie_rawRes="@raw/sfps_pulse"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_authenticating.json b/packages/SystemUI/res/raw/face_dialog_authenticating.json
index 4e25e6d..896816e 100644
--- a/packages/SystemUI/res/raw/face_dialog_authenticating.json
+++ b/packages/SystemUI/res/raw/face_dialog_authenticating.json
@@ -1 +1 @@
-{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":64,"h":64,"nm":"face_scanning 3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[27.25,27.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[95,95,100]},{"t":60,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.244,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.244,0]],"v":[[-2.249,0.001],[0.001,2.251],[2.249,0.001],[0.001,-2.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[15.1,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.242,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.242,0]],"v":[[-2.249,0],[0.001,2.25],[2.249,0],[0.001,-2.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.4,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.814,3.523],[-2.814,3.523],[-2.814,1.363],[0.652,1.363],[0.652,-3.523],[2.814,-3.523]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.791,28.479],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.154,0.15],[0,0],[0.117,-0.095],[0,0],[0.228,-0.121],[0.358,-0.103],[0.922,0.261],[0.3,0.16],[0.24,0.185],[0.14,0.139],[0.178,0.261],[0.143,0.451],[0,0],[0,0.494],[0,0],[-0.214,-0.676],[-0.392,-0.572],[-0.323,-0.317],[-0.228,-0.177],[-0.333,-0.179],[-0.503,-0.145],[-0.662,0],[-0.653,0.184],[-0.437,0.233],[-0.336,0.258],[0,0],[0,0]],"o":[[0,0],[-0.107,0.106],[0,0],[-0.24,0.185],[-0.301,0.16],[-0.92,0.261],[-0.357,-0.103],[-0.228,-0.121],[-0.158,-0.122],[-0.225,-0.221],[-0.272,-0.393],[0,0],[-0.147,-0.466],[0,0],[0,0.716],[0.206,0.656],[0.256,0.372],[0.204,0.201],[0.336,0.258],[0.436,0.233],[0.655,0.184],[0.662,0],[0.503,-0.145],[0.332,-0.179],[0,0],[0,0],[0.165,-0.136]],"v":[[6.094,1.465],[4.579,-0.076],[4.242,0.225],[4.124,0.315],[3.43,0.771],[2.439,1.165],[-0.342,1.165],[-1.331,0.771],[-2.027,0.315],[-2.48,-0.075],[-3.087,-0.801],[-3.712,-2.075],[-3.712,-2.075],[-3.934,-3.523],[-6.094,-3.523],[-5.771,-1.424],[-4.868,0.424],[-3.995,1.465],[-3.344,2.027],[-2.35,2.676],[-0.934,3.243],[1.049,3.523],[3.031,3.243],[4.449,2.676],[5.441,2.027],[5.482,1.997],[5.615,1.895]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.201,40.411],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.398,0],[0,-13.4],[13.398,0],[0,13.4]],"o":[[13.398,0],[0,13.4],[-13.398,0],[0,-13.4]],"v":[[0,-24.3],[24.3,0],[0,24.3],[-24.3,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[14.904,0],[0,-14.904],[-14.904,0],[0,14.904]],"o":[[-14.904,0],[0,14.904],[14.904,0],[0,-14.904]],"v":[[0,-27],[-27,0],[0,27],[27,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.25,27.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1200,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
+{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":68,"h":68,"nm":"face_dialog_authenticating","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[27.25,27.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[95,95,100]},{"t":60,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.244,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.244,0]],"v":[[-2.249,0.001],[0.001,2.251],[2.249,0.001],[0.001,-2.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[15.1,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.242,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.242,0]],"v":[[-2.249,0],[0.001,2.25],[2.249,0],[0.001,-2.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.4,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.814,3.523],[-2.814,3.523],[-2.814,1.363],[0.652,1.363],[0.652,-3.523],[2.814,-3.523]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.791,28.479],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.154,0.15],[0,0],[0.117,-0.095],[0,0],[0.228,-0.121],[0.358,-0.103],[0.922,0.261],[0.3,0.16],[0.24,0.185],[0.14,0.139],[0.178,0.261],[0.143,0.451],[0,0],[0,0.494],[0,0],[-0.214,-0.676],[-0.392,-0.572],[-0.323,-0.317],[-0.228,-0.177],[-0.333,-0.179],[-0.503,-0.145],[-0.662,0],[-0.653,0.184],[-0.437,0.233],[-0.336,0.258],[0,0],[0,0]],"o":[[0,0],[-0.107,0.106],[0,0],[-0.24,0.185],[-0.301,0.16],[-0.92,0.261],[-0.357,-0.103],[-0.228,-0.121],[-0.158,-0.122],[-0.225,-0.221],[-0.272,-0.393],[0,0],[-0.147,-0.466],[0,0],[0,0.716],[0.206,0.656],[0.256,0.372],[0.204,0.201],[0.336,0.258],[0.436,0.233],[0.655,0.184],[0.662,0],[0.503,-0.145],[0.332,-0.179],[0,0],[0,0],[0.165,-0.136]],"v":[[6.094,1.465],[4.579,-0.076],[4.242,0.225],[4.124,0.315],[3.43,0.771],[2.439,1.165],[-0.342,1.165],[-1.331,0.771],[-2.027,0.315],[-2.48,-0.075],[-3.087,-0.801],[-3.712,-2.075],[-3.712,-2.075],[-3.934,-3.523],[-6.094,-3.523],[-5.771,-1.424],[-4.868,0.424],[-3.995,1.465],[-3.344,2.027],[-2.35,2.676],[-0.934,3.243],[1.049,3.523],[3.031,3.243],[4.449,2.676],[5.441,2.027],[5.482,1.997],[5.615,1.895]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.201,40.411],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.398,0],[0,-13.4],[13.398,0],[0,13.4]],"o":[[13.398,0],[0,13.4],[-13.398,0],[0,-13.4]],"v":[[0,-24.3],[24.3,0],[0,24.3],[-24.3,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[14.904,0],[0,-14.904],[-14.904,0],[0,14.904]],"o":[[-14.904,0],[0,14.904],[14.904,0],[0,-14.904]],"v":[[0,-27],[-27,0],[0,27],[27,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.25,27.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1200,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_dark_to_checkmark.json b/packages/SystemUI/res/raw/face_dialog_dark_to_checkmark.json
new file mode 100644
index 0000000..512ca30
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_dark_to_checkmark.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":23,"w":68,"h":68,"nm":"face_dialog_dark_to_checkmark","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".green100","cl":"green100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-116,-16.5],[-31.25,68.5],[108.75,-71.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.155],"y":[1]},"o":{"x":[0.292],"y":[0]},"t":2,"s":[0]},{"t":16,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.76862745098,0.933333333333,0.81568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.1,30.083],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.537,0.537],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":2,"s":[8,8]},{"i":{"x":[0.533,0.533],"y":[1,1]},"o":{"x":[0.441,0.441],"y":[0,0]},"t":12,"s":[12.789,12.789]},{"i":{"x":[0.486,0.486],"y":[1,1]},"o":{"x":[0.424,0.424],"y":[0,0]},"t":18,"s":[12.241,12.241]},{"t":22,"s":[12.5,12.5]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkmark","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".green100","cl":"green100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.768627464771,0.933333337307,0.815686285496,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[0]},{"t":7,"s":[100]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.780392156863,0.980392156863,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[100]},{"t":7,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.727,31.703],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.287,0.287],"y":[0.12,0.12]},"t":0,"s":[100,100]},{"t":5,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.6,23.8],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":5,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.1,23.8],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":5,"s":[0,0]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":5,"s":[50]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[100]},{"t":5,"s":[50]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":3,"s":[0]}],"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.094,31.756],"ix":2},"a":{"a":0,"k":[1.05,-9.891],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[65,65]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":23,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_dark_to_error.json b/packages/SystemUI/res/raw/face_dialog_dark_to_error.json
new file mode 100644
index 0000000..9578cb4
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_dark_to_error.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":20,"w":68,"h":68,"nm":"face_dialog_dark_to_error","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".red100","cl":"red100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-13.15],[0.9,-13.15]],"c":true}]},{"t":19,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"s":[30.3,31.45],"to":[0,-0.373],"ti":[0,0.373]},{"t":13,"s":[30.3,29.215]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[18.6,23.8],"to":[0.568,-0.715],"ti":[-0.568,0.715]},{"t":4,"s":[22.005,19.51]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[41.1,23.8],"to":[-0.552,-0.712],"ti":[0.552,0.712]},{"t":4,"s":[37.788,19.53]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[34]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[57]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2]},{"t":13,"s":[2.5]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[29.044,41.646],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":4,"op":20,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.667,106.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":13,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-13.15],[0.9,-13.15]],"c":true}]},{"t":19,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":4,"s":[30.3,31.45],"to":[0,-0.373],"ti":[0,0.373]},{"t":13,"s":[30.3,29.215]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[18.6,23.8],"to":[0.568,-0.715],"ti":[-0.568,0.715]},{"t":4,"s":[22.005,19.51]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[100]},{"t":4,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[41.1,23.8],"to":[-0.552,-0.712],"ti":[0.552,0.712]},{"t":4,"s":[37.788,19.53]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.999,0.999],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"t":4,"s":[30,30]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[34]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[57]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2]},{"t":13,"s":[2.5]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[29.044,41.646],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0.658823549747,0.780392169952,0.980392158031,1]},{"t":4,"s":[0.949019610882,0.721568644047,0.709803938866,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":4,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_error_to_idle.json b/packages/SystemUI/res/raw/face_dialog_error_to_idle.json
new file mode 100644
index 0000000..f944314
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_error_to_idle.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":14,"w":68,"h":68,"nm":"face_error_to_idle","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.67,"y":1},"o":{"x":0.321,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]},{"i":{"x":0.456,"y":1},"o":{"x":0.568,"y":0},"t":5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"t":13,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[30.3,29.215],"to":[0,0.373],"ti":[0,-0.373]},{"t":9,"s":[30.3,31.45]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[22.005,19.51],"to":[-0.568,0.715],"ti":[0.568,-0.715]},{"t":13,"s":[18.6,23.8]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[37.788,19.53],"to":[0.552,0.712],"ti":[-0.552,-0.712]},{"t":13,"s":[41.1,23.8]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[34]},{"t":13,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[57]},{"t":13,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]},{"t":7,"s":[0.658823549747,0.780392169952,0.980392158031,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2.5]},{"t":13,"s":[2]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[29.044,41.646],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]},{"t":7,"s":[0.658823549747,0.780392169952,0.980392158031,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":6,"op":14,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".red100","cl":"red100","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.67,"y":1},"o":{"x":0.321,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-11.712],[0.9,-11.712]],"c":true}]},{"i":{"x":0.456,"y":1},"o":{"x":0.568,"y":0},"t":5,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0.9,3.25],[-1.5,3.25],[-1.5,1.25],[-1.5,1.25],[-1.5,-3.25],[0.9,-3.25]],"c":true}]},{"t":13,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]}],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[30.3,29.215],"to":[0,0.373],"ti":[0,-0.373]},{"t":9,"s":[30.3,31.45]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[22.005,19.51],"to":[-0.568,0.715],"ti":[0.568,-0.715]},{"t":13,"s":[18.6,23.8]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176486015,0.75686275959,0.776470601559,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":6,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":3,"s":[37.788,19.53],"to":[0.552,0.712],"ti":[-0.552,-0.712]},{"t":13,"s":[41.1,23.8]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":3,"s":[30,30]},{"t":10,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":0,"s":[{"i":[[0,0],[1.534,0],[1.832,-0.006]],"o":[[-0.948,-0.005],[-3.033,0],[0,0]],"v":[[5.734,-2.889],[0.961,-2.894],[-4.74,-2.887]],"c":false}]},{"t":13,"s":[{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[34]},{"t":13,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[57]},{"t":13,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":0,"s":[2.5]},{"t":13,"s":[2]}],"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[29.044,41.646],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0.949019610882,0.721568644047,0.709803938866,1]},{"t":6,"s":[0.741176486015,0.75686275959,0.776470601559,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":6,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_idle_static.json b/packages/SystemUI/res/raw/face_dialog_idle_static.json
new file mode 100644
index 0000000..2d3a7a4
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_idle_static.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":14,"w":68,"h":68,"nm":"face_dialog_idle_static","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.3,31.45],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.6,23.8],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.1,23.8],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[29.044,41.646],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":14,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/face_dialog_wink_from_dark.json b/packages/SystemUI/res/raw/face_dialog_wink_from_dark.json
new file mode 100644
index 0000000..05b55c9
--- /dev/null
+++ b/packages/SystemUI/res/raw/face_dialog_wink_from_dark.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":68,"h":68,"nm":"face_dialog_wink_from_dark","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","cl":"blue200","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.305],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":26,"s":[-14]},{"t":45,"s":[0]}],"ix":10},"p":{"a":0,"k":[30,30,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.4],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.305,0.305,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":26,"s":[106,106,100]},{"t":45,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.6,3.25],[-2.6,3.25],[-2.6,1.25],[0.6,1.25],[0.6,-3.25],[2.6,-3.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.727,31.703],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Nose","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.2,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.6,23.8],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":0,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":26,"s":[100,26]},{"t":45,"s":[100,100]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_L","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.2],[-1.2,0],[0,1.2],[1.2,0]],"o":[[0,1.2],[1.1,0],[0,-1.2],[-1.2,0]],"v":[[-2.1,0],[0,2.1],[2.1,0],[0,-2.1]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823549747,0.780392169952,0.980392158031,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.1,23.8],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Eye_R\r","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.534,0],[0,3.034]],"o":[[-0.996,1.015],[-3.033,0],[0,0]],"v":[[4.705,1.103],[0.787,2.747],[-4.705,-2.747]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.780392156863,0.980392156863,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.094,31.756],"ix":2},"a":{"a":0,"k":[1.05,-9.891],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mouth","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":80,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue200","cl":"blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[34,34,0],"ix":2,"l":2},"a":{"a":0,"k":[30,30,0],"ix":1,"l":2},"s":{"a":0,"k":[106.329,106.329,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.1,0],[0,-13.2],[-13.1,0],[0,13.2]],"o":[[-13.1,0],[0,13.2],[13.1,0],[0,-13.2]],"v":[[-0.05,-23.8],[-23.75,0],[-0.05,23.8],[23.75,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.780392156863,0.980392156863,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[30.05,30],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Outline","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":80,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index c775336..e38da09 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Jy sal ophou om <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> op te neem"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Jy sal ophou om <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> te deel"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Jy sal ophou om <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uit te saai"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Kenmerke soos Kitsdeel en Kry My Toestel gebruik Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deel oudio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deel tans oudio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
@@ -378,7 +379,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skermopname"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Begin"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Neem kwessie op"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Teken kwessie aan"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Begin"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Foutverslag"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"kies legstuk"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"verwyder legstuk"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plaas gekose legstuk"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aftrekkieslys"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Begin nou"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Geen kennisgewings nie"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nuwe kennisgewings nie"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontsluit om ouer kennisgewings te sien"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Hierdie toestel word deur jou ouer bestuur"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Jou organisasie besit hierdie toestel en kan netwerkverkeer monitor"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tik vir meer inligting"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Geen wekker nie"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"voer skermslot in"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Raak die vingerafdruksensor. Dis die korter knoppie aan die sykant van die foon"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Vingerafdruksensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"staaf"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"gaan by toestel in"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index de4138f..6b024e2 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"እርስዎ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> መቅረጽ ያቆማሉ"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"እርስዎ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ማጋራት ያቆማሉ"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"እርስዎ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> cast ማድረግ ያቆማሉ"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -487,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ምግብር ይምረጡ"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ምግብር አስወግድ"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"በቦታ የተመረጠ ምግብር"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ወደታች ተጎታች ምናሌ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
@@ -540,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"አሁን ጀምር"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ምንም ማሳወቂያ የለም"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ምንም አዲስ ማሳወቂያዎች የሉም"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"የቆዩ ማሳወቂያዎችን ለማየት ይክፈቱ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ይህ መሣሪያ በእርስዎ ወላጅ የሚተዳደር ነው።"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"የእርስዎ ድርጅት የዚህ መሣሪያ ባለቤት ነው፣ እና የአውታረ መረብ ትራፊክን ሊከታተል ይችላል"</string>
@@ -1195,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ለበለጠ መረጃ መታ ያድርጉ"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ምንም ማንቂያ አልተቀናበረም"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ማያ ገጽ መቆለፊያ ያስገቡ"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"የጣት አሻራ ዳሳሹን ይንኩ። በስልኩ ጎን በኩል አጠር ያለው አዝራር ነው"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"የጣት አሻራ ዳሳሽ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ያረጋግጡ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"መሣሪያን ያስገቡ"</string>
@@ -1353,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 8bd52d6..610056d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"سيتم إيقاف تسجيل محتوى <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"سيتم إيقاف مشاركة محتوى <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"سيتم إيقاف بث محتوى <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\""</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"مشاركة الصوت"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"جارٍ مشاركة الصوت"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"اختيار التطبيق المصغّر"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"إزالة التطبيق المصغّر"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"إضافة التطبيق المصغّر المحدَّد"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"البدء الآن"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ما مِن إشعارات"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ما مِن إشعارات جديدة"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"\"الإشعارات التكيّفية\" مفعَّلة"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"عند تلقّي إشعارات متعددة في فترة زمنية قصيرة، يخفِّض جهازك الصوت ويقلّل من ظهور النوافذ المنبثقة على الشاشة لمدة تصل إلى دقيقتين."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"إيقاف"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"افتَح قفل الشاشة لعرض الإشعارات الأقدم."</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"يتولّى أحد الوالدين إدارة هذا الجهاز."</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"تملك مؤسستك هذا الجهاز ويمكنها تتبّع حركة بيانات الشبكة."</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"انقر للحصول على مزيد من المعلومات."</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"لم يتم ضبط منبّه"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"إدخال الرمز لفتح القفل"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"المس أداة استشعار بصمة الإصبع المدمَجة في الزر الأقصر في جانب الهاتف"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"مستشعر بصمات الإصبع"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"المصادقة"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"الدخول إلى الجهاز"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"رجوع"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"للرجوع، مرِّر سريعًا لليسار أو لليمين باستخدام ثلاثة أصابع في أي مكان على لوحة اللمس."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"لوحة لمس تعرض ثلاثة أصابع تتحرك يمينًا ويسارًا"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"شاشة جهاز تعرض صورة متحركة لإيماءة الرجوع"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"مستوى الإضاءة: %1$d من %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 77774cc..764f4de 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"আপুনি <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ৰেকৰ্ড কৰা বন্ধ কৰিব"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"আপুনি <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> শ্বেয়াৰ কৰা বন্ধ কৰিব"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"আপুনি <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> কাষ্ট কৰা বন্ধ কৰিব"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share আৰু Find My Deviceৰ দৰে সুবিধাসমূহে ব্লুটুথ ব্যৱহাৰ কৰে"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিঅ’ শ্বেয়াৰ কৰক"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ৱিজেট বাছনি কৰক"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ৱিজেট আঁতৰাওক"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"বাছনি কৰা ৱিজেটটো ৰাখক"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যৱহাৰকাৰী সলনি কৰক"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুল-ডাউনৰ মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"এতিয়াই আৰম্ভ কৰক"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"কোনো জাননী নাই"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"কোনো নতুন জাননী নাই"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"অভিযোজিত জাননী অন কৰা আছে"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"আপোনাৰ ডিভাইচে এতিয়া ভলিউম হ্ৰাস কৰে আৰু আপুনি কম সময়ৰ ভিতৰত বহুতো জাননী পালে দুই মিনিটলৈকে স্ক্ৰীনত ওলোৱা পপ-আপ হ্ৰাস কৰে।"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"অফ কৰক"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"পুৰণি জাননী চবলৈ আনলক কৰক"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"এই ডিভাইচটো আপোনাৰ অভিভাৱকে পৰিচালনা কৰে"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"এই ডিভাইচটোৰ গৰাকী আপোনাৰ প্ৰতিষ্ঠান আৰু ই নেটৱৰ্কৰ ট্ৰেফিক নিৰীক্ষণ কৰিব পাৰে"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"অধিক তথ্যৰ বাবে টিপক"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"কোনো এলাৰ্ম ছেট কৰা হোৱা নাই"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"স্ক্ৰীন লকটো দিয়ক"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক। এইটো হৈছে আপোনাৰ ফ’নটোৰ কাষৰফালে থকা চুটি বুটামটো"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"বিশ্বাসযোগ্যতা প্ৰমাণ কৰক"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইচ আনলক কৰক"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"উভতি যাওক"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"উভতি যাবলৈ, টাচ্চপেডৰ যিকোনো স্থানত তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁ বা সোঁফালে ছোৱাইপ কৰক।"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"তিনিটা আঙুলি সোঁ আৰু বাওঁফালে লৰচৰ কৰা দেখুওৱা টাচ্চপেড"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"পিছফালৰ নিৰ্দেশৰ এনিমেশ্বন দেখুওৱা ডিভাইচ স্ক্ৰীন"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index bf24c07..9c50f46 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinin çəkilməsini dayandıracaqsınız"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinin paylaşılmasını dayandıracaqsınız"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinin yayımlanmasını dayandıracaqsınız"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Cəld Paylaşım və Cihazın Tapılması kimi funksiyalar Bluetooth istifadə edir"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sabah səhər aktiv ediləcək"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audio paylaşın"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio paylaşılır"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidcet seçin"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"vidceti silin"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"seçilmiş vidceti yerləşdirin"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"aşağı çəkilən menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"İndi başlayın"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Heç bir bildiriş yoxdur"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Yeni bildiriş yoxdur"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptiv bildirişlər aktivdir"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Qısa vaxtda çoxlu bildiriş alanda cihaz səsi azaldır, ekranda popapları iki dəqiqəyə qədər qısaldır."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Deaktiv edin"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Köhnə bildirişləri görmək üçün kilidi açın"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Bu cihaz valideyniniz tərəfindən idarə olunur"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Təşkilatınız bu cihazın sahibidir və şəbəkə trafikinə nəzarət edə bilər"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ətraflı məlumat üçün toxunun"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Siqnal ayarlanmayıb"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ekran kilidi daxil edin"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Barmaq izi sensoruna toxunun. Telefonun yan tərəfindəki qısa düymədir"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Barmaq izi sensoru"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"doğrulayın"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz daxil edin"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Geri qayıtmaq üçün taçpedin istənilən yerində üç barmaqla sola və ya sağa çəkin."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Üç barmağın sağa və sola hərəkət etdiyini göstərən taçped"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Geri jesti üçün animasiya göstərən cihaz ekranı"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 02271f0..034102c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Zaustavićete snimanje za: <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Zaustavićete deljenje za: <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Zaustavićete prebacivanje za: <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutru"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deli zvuk"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deli se zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"izaberite vidžet"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"uklonite vidžet"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavite izabrani vidžet"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nema obaveštenja"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obaveštenja"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Prilag. obaveštenja su uključena"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Uređaj sada smanjuje zvuk i broj iskačućih prozora na ekranu do 2 minuta kad primite mnogo obaveštenja u kratkom roku."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starija obaveštenja"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Ovim uređajem upravlja roditelj"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizacija je vlasnik uređaja i može da nadgleda mrežni saobraćaj"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nije podešen"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"unesite otključavanje ekrana"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dodirnite senzor za otisak prsta. To je kraće dugme na bočnoj strani telefona"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor za otisak prsta"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"potvrdite identitet"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"unesite uređaj"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Da biste se vratili, prevucite ulevo ili udesno sa tri prsta bilo gde na tačpedu."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tačped sa prikazom tri prsta koji se pomeraju udesno i ulevo"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekran uređaja sa prikazom animacije pokreta za nazad"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index ed44819..c2f2cd0 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Запіс з праграмы <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> будзе спынены"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Абагульванне з праграмы <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> будзе спынена"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Трансляцыя праграмы <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> будзе спынена"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth выкарыстоўваецца такімі функцыямі і сэрвісамі, як Хуткае абагульванне і Знайсці прыладу"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Абагуліць аўдыя"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ідзе абагульванне аўдыя"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -379,7 +380,7 @@
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Пачаць"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Спыніць"</string>
<string name="qs_record_issue_label" msgid="8166290137285529059">"Запіс праблемы"</string>
- <string name="qs_record_issue_start" msgid="2979831312582567056">"Пачынайце"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Пачаць"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Спыніцеся"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Справаздача"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З чым была звязана праблема, якая вам сустрэлася?"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"выбраць віджэт"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"выдаліць віджэт"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"размясціць выбраны віджэт"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Перайсці да іншага карыстальніка"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"высоўнае меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Апавяшчэнняў няма"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Няма новых апавяшчэнняў"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблакіруйце, каб убачыць усе апавяшчэнні"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Гэта прылада знаходзіцца пад кантролем бацькоў"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ваша арганізацыя валодае гэтай прыладай і можа кантраляваць сеткавы трафік"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Націсніце, каб убачыць больш"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Няма будзільнікаў"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"прыступіць да разблакіроўкі экрана"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Дакраніцеся да сканера адбіткаў пальцаў. Гэта кароткая кнопка на баку вашага тэлефона"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер адбіткаў пальцаў"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"правесці аўтэнтыфікацыю"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"адкрыць галоўны экран прылады"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index b88e0e4..8111777 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Ще спрете да записвате <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Ще спрете да споделяте <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Ще спрете да предавате <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функции като „Бързо споделяне“ и „Намиране на устройството ми“ използват Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделяне на звука"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Звукът се споделя"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"избиране на приспособление"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"премахване на приспособлението"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставяне на избраното приспособление"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Превключване между потребителите"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падащо меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Стартиране сега"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Няма известия"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Няма нови известия"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Адаптивните известия са вкл."</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"У-вото ви намалява силата на звука и броя на изскачащите прозорци за период до 2 мин, ако получавате много известия за кратко време."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Изключване"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Отключете за достъп до по-стари известия"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Това устройство се управлява от родителя ви"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Организацията ви притежава това устройство и може да наблюдава трафика в мрежата"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Докоснете за още информация"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Няма зададен будилник"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"въведете опция за заключване на екрана"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Докоснете сензора за отпечатъци. Това е по-късият бутон отстрани на телефона"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отпечатъци"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"удостоверяване"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"вход в устройството"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"За да се върнете назад, прекарайте три пръста наляво или надясно по сензорния панел."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Сензорен панел, върху който три пръста се движат надясно и наляво"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран на устройство, показващ анимация за жеста за връщане назад"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за дома"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 0e44b3c..d3571b0 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"এটির জন্য <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপের কন্টেন্ট রেকর্ড হওয়া বন্ধ হয়ে যাবে"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"এটির জন্য <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপের কন্টেন্ট শেয়ার করা বন্ধ হয়ে যাবে"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"এটির জন্য <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপের কন্টেন্ট কাস্ট হওয়া বন্ধ হয়ে যাবে"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"দ্রুত শেয়ার ও Find My Device-এর মতো ফিচার ব্লুটুথ ব্যবহার করে"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিও শেয়ার করুন"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিও শেয়ার করা হচ্ছে"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"উইজেট বেছে নিন"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"উইজেট সরান"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"বেছে নেওয়া উইজেটটি রাখুন"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"এখন শুরু করুন"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"কোনও বিজ্ঞপ্তি নেই"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"নতুন কোনও বিজ্ঞপ্তি নেই"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"অ্যাডাপ্টিভ বিজ্ঞপ্তি চালু আছে"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"অল্প সময়ে অনেক বেশি বিজ্ঞপ্তি পেলে, আপনার ডিভাইস এখন ২ মিনিটের জন্য ভলিউম ও স্ক্রিনে আসা পপ-আপ কমায়।"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"বন্ধ করুন"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"পুরনো বিজ্ঞপ্তি দেখতে আনলক করুন"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"আপনার অভিভাবক এই ডিভাইস ম্যানেজ করেন"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের এবং এরা ডিভাইসের নেটওয়ার্ক ট্রাফিক মনিটর করতে পারে"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"আরও তথ্যের জন্য ট্যাপ করুন"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"কোনও অ্যালার্ম সেট করা নেই"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"স্ক্রিন লক খুলুন"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ফিঙ্গারপ্রিন্ট সেন্সর টাচ করুন। এটি ফোনের পাশের দিকে থাকা তুলনামূলক ছোট বোতাম"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ফিঙ্গারপ্রিন্ট সেন্সর"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"যাচাই করিয়ে নিন"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইস আনলক করুন"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ফিরে যান"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ফিরে যেতে, টাচপ্যাডের যেকোনও জায়গায় তিনটি আঙুল দিয়ে ডান বা বাঁদিকে সোয়াইপ করুন।"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"টাচপ্যাডে, তিনটি আঙুল ডান ও বাঁদিকে সরানো দেখানো হচ্ছে"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ডিভাইসের স্ক্রিনে ফিরে যাওয়ার জেসচারের অ্যানিমেশন দেখানো হচ্ছে"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index a7f9068..b3fcf1ad 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Zaustavit ćete snimanje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Zaustavit ćete dijeljenje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Zaustavit ćete emitiranje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -304,7 +307,7 @@
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string>
<string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dijeli zvuk"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zajedničko slušanje"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Dijeljenje zvuka"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -376,7 +379,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Započnite"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježite problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvještaj o grešci"</string>
@@ -487,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"odabir vidžeta"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"uklanjanje vidžeta"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavljanje odabranog vidžeta"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
@@ -540,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Započni odmah"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nema obavještenja"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavještenja"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Prilagodljive obavijesti uklj."</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Uređaj sada stišava zvuk i smanjuje broj skočnih prozora na zaslonu na 2 minute kada primite jako puno obavijesti u kratkom vremenskom razdoblju."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte da vidite starija obavještenja"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Ovim uređajem upravlja tvoj roditelj"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša organizacija je vlasnik ovog uređaja i može nadzirati mrežni saobraćaj"</string>
@@ -778,7 +790,7 @@
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"ili"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Brisanje upita za pretraživanje"</string>
<string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Prečice na tastaturi"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretraživanje prečica"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Pretražite prečice"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Nisu pronađene prečice"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Sistem"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Unos"</string>
@@ -1195,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nema nijednog alarma"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"unos zaključavanja ekrana"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dodirnite senzor za otisak prsta. To je kraće dugme na bočnoj strani telefona"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor za otisak prsta"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificiranje"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pristup uređaju"</string>
@@ -1353,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Za povratak trima prstima prijeđite ulijevo ili udesno bilo gdje na dodirnoj podlozi."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Dodirna podloga prikazuje tri prsta koji se kreću udesno i ulijevo"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Na zaslonu uređaja prikazuje se animacija za pokret za povratak"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 4f055d4..c4879c4 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Deixaràs de gravar <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Deixaràs de compartir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Deixaràs d\'emetre <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les funcions com Quick Share i Troba el meu dispositiu utilitzen el Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth s\'activarà demà al matí"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Comparteix l\'àudio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"S\'està compartint l\'àudio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string>
@@ -382,7 +383,7 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"Inicia"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Atura"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe d\'errors"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quina part de l\'experiència s\'ha vist afectada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string>
<string name="performance" msgid="6552785217174378320">"Rendiment"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecciona el widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"suprimeix el widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"col·loca el widget seleccionat"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Comença ara"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No hi ha cap notificació"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No hi ha cap notificació nova"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueja per veure notif. anteriors"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Els teus pares gestionen aquest dispositiu"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"La teva organització és propietària del dispositiu i és possible que supervisi el trànsit de xarxa"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca per obtenir més informació"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Cap alarma definida"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"utilitza el bloqueig de pantalla"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toca el sensor d\'empremtes digitals. És el botó més curt del lateral del telèfon."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor d\'empremtes digitals"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accedir al dispositiu"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index e50fea5..c0db5aa 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -107,7 +107,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Začít nahrávat?"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Během nahrávání má Android přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Během nahrávání má Android přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Dejte proto pozor na hesla, platební údaje, zprávy, fotografie, zvukové záznamy nebo videa."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Během nahrávání aplikace má Android přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s informacemi, jako jsou hesla, platební údaje, zprávy, fotky, zvukové záznamy nebo videa."</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Začít nahrávat"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrávat zvuk"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Přestane se nahrávat aplikace <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Přestane se sdílet aplikace <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Přestane se odesílat aplikace <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth využívají funkce jako Quick Share a Najdi moje zařízení."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sdílet zvuk"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zvuk se sdílí"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string>
@@ -383,7 +384,7 @@
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Ukončit"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Zpráva o chybě"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string>
- <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problému"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
<string name="performance" msgid="6552785217174378320">"Výkon"</string>
<string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vybrat widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstranit widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"umístit vybraný widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbalovací nabídka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustit"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Žádná oznámení"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Žádná nová oznámení"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Starší oznámení se zobrazí po odemknutí"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Toto zařízení spravuje rodič"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Toto zařízení vlastní vaše organizace, která může sledovat síťový provoz"</string>
@@ -618,11 +631,11 @@
<string name="screen_pinning_negative" msgid="6882816864569211666">"Ne, děkuji"</string>
<string name="screen_pinning_start" msgid="7483998671383371313">"Aplikace byla připnuta"</string>
<string name="screen_pinning_exit" msgid="4553787518387346893">"Aplikace byla odepnuta"</string>
- <string name="stream_voice_call" msgid="7468348170702375660">"Hovor"</string>
+ <string name="stream_voice_call" msgid="7468348170702375660">"Hovory"</string>
<string name="stream_system" msgid="7663148785370565134">"Systém"</string>
<string name="stream_ring" msgid="7550670036738697526">"Vyzvánění"</string>
<string name="stream_music" msgid="2188224742361847580">"Média"</string>
- <string name="stream_alarm" msgid="16058075093011694">"Budík"</string>
+ <string name="stream_alarm" msgid="16058075093011694">"Budíky"</string>
<string name="stream_notification" msgid="7930294049046243939">"Oznámení"</string>
<string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
<string name="stream_dtmf" msgid="7322536356554673067">"Tónová volba"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Klepnutím zobrazíte další informace"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Budík nenastaven"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"zadejte zámek obrazovky"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dotkněte se snímače otisků prstů. Vypínač je kratší tlačítko na boku telefonu."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Snímač otisků prstů"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ověříte"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"zadáte zařízení"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 310f2b1..cce4615 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Du stopper optagelse af <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Du stopper deling af <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Du stopper casting af <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som f.eks. Quick Share og Find min enhed anvender Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveres i morgen tidlig"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -378,7 +379,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skærmoptagelse"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Optag problem"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrer problem"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fejlrapport"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vælg widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjern widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placer valgt widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullemenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nu"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ingen notifikationer"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ingen nye notifikationer"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås op for at se ældre notifikationer"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Denne enhed administreres af din forælder"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Din organisation ejer denne enhed og overvåger muligvis netværkstrafikken"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tryk for at få flere oplysninger"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ingen alarm er indstillet"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"angiv skærmlås"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Sæt fingeren på fingeraftrykssensoren. Det er den mindste knap på siden af telefonen"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeraftrykssensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"godkende"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"få adgang til enheden"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 27f6b57..31ac8e8 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> wird dann nicht mehr aufgezeichnet"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> wird dann nicht mehr geteilt"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> wird dann nicht mehr übertragen"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktionen wie „Quick Share“ und „Mein Gerät finden“ verwenden Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wird morgen früh aktiviert"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioinhalte freigeben"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioinhalte werden freigegeben"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -379,10 +380,10 @@
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string>
<string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string>
- <string name="qs_record_issue_start" msgid="2979831312582567056">"Aufnahme starten"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fehlerbericht"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Probleme sind bei deinem Gerät aufgetreten?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string>
<string name="performance" msgid="6552785217174378320">"Leistung"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"Widget auswählen"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"Widget entfernen"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ausgewähltes Widget in den Bearbeitungsmodus versetzen"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Nutzer wechseln"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Pull-down-Menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Jetzt starten"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Keine Benachrichtigungen"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Keine neuen Benachrichtigungen"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Für ältere Benachrichtigungen entsperren"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dieses Gerät wird von deinen Eltern verwaltet"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Deine Organisation verwaltet dieses Gerät und kann den Netzwerkverkehr überwachen"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Für weitere Informationen tippen"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Kein Wecker gestellt"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"Displaysperre eingeben"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Berühre den Fingerabdrucksensor. Das ist die kürzere Taste an der Seite des Smartphones."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerabdrucksensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authentifizieren"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"Eingeben des Geräts"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index e2480b2e..80a836a 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Θα διακόψετε την εγγραφή της εφαρμογής <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Θα διακόψετε την κοινή χρήση της εφαρμογής <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Θα διακόψετε τη μετάδοση της εφαρμογής <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Λειτουργίες όπως το Quick Share και η Εύρεση συσκευής χρησιμοποιούν το Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Κοινή χρήση ήχου"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Κοινή χρήση ήχου σε εξέλιξη"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -390,7 +391,7 @@
<string name="thermal" msgid="6758074791325414831">"Θερμικό"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string>
- <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργά"</string>
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργές"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Αποσυνδεδεμένα"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Συσκευές ακοής"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Σύζευξη νέας συσκευής"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"επιλογή γραφικού στοιχείου"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"κατάργηση γραφικού στοιχείου"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"τοποθέτηση επιλεγμένου γραφικού στοιχείου"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"αναπτυσσόμενο μενού"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Δεν υπάρχουν ειδοποιήσεις"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Δεν υπάρχουν νέες ειδοποιήσεις"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ξεκλειδώστε για εμφάνιση παλαιότ. ειδοπ."</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Αυτή η συσκευή είναι διαχειριζόμενη από τον γονέα σου"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ο οργανισμός σας κατέχει αυτήν τη συσκευή και μπορεί να παρακολουθεί την επισκεψιμότητα δικτύου."</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Πατήστε για περισσότερες πληροφορίες."</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Δεν ορίστηκε ξυπνητ."</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"εισαγωγή κλειδώματος οθόνης"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων. Είναι το πιο κοντό κουμπί στην πλευρά του τηλεφώνου."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Αισθητήρας δακτυλικών αποτυπωμάτων"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"έλεγχος ταυτότητας"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"εισαγωγή συσκευής"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 303e922..7c6262c 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"You will stop recording <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"You will stop sharing <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"You will stop casting <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to 2 minutes when you receive many notifications in a short time span."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organisation owns this device and may monitor network traffic"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Touch the fingerprint sensor. It\'s the shorter button on the side of the phone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using 3 fingers anywhere on the touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 042d06e..ed4ccf3 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -125,26 +125,19 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
- <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
- <skip />
- <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
- <skip />
- <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
- <skip />
- <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
- <skip />
- <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
- <skip />
- <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
- <skip />
- <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
- <skip />
- <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
- <skip />
- <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
- <skip />
- <!-- no translation found for close_dialog_button (4749497706540104133) -->
- <skip />
+ <string name="screenrecord_stop_dialog_title" msgid="2685522129492260887">"Stop recording screen?"</string>
+ <string name="screenrecord_stop_dialog_message" msgid="1926783607059442889">"You will stop recording your screen"</string>
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"You will stop recording <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
+ <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
+ <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+ <string name="share_to_app_stop_dialog_message" msgid="3181723638915877339">"You will stop sharing your screen"</string>
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"You will stop sharing <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
+ <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
+ <string name="cast_to_other_device_stop_dialog_title" msgid="1910372600290258193">"Stop casting screen?"</string>
+ <string name="cast_to_other_device_stop_dialog_message" msgid="1502520537030715412">"You will stop casting your screen"</string>
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"You will stop casting <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
+ <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stop casting"</string>
+ <string name="close_dialog_button" msgid="4749497706540104133">"Close"</string>
<string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
<string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
<string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
@@ -155,8 +148,7 @@
<string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
<string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
- <!-- no translation found for immersive_cling_description (2717426731830851921) -->
- <skip />
+ <string name="immersive_cling_description" msgid="2717426731830851921">"To exit, swipe down from the top of your screen"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
<string name="accessibility_back" msgid="6530104400086152611">"Back"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -299,8 +291,7 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Automatically turn on tomorrow"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
<string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
@@ -487,6 +478,9 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
+ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
+ <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
+ <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -540,6 +534,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to two minutes when you receive many notifications in a short time span."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organization owns this device and may monitor network traffic"</string>
@@ -1195,6 +1192,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Touch the fingerprint sensor. It’s the shorter button on the side of the phone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1345,14 +1343,14 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for touchpad_tutorial_back_gesture_button (2746834288077265946) -->
- <skip />
- <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
- <skip />
- <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
- <skip />
- <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
- <skip />
+ <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
+ <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
+ <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using three fingers anywhere on the touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing three fingers moving right and left"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 303e922..7c6262c 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"You will stop recording <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"You will stop sharing <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"You will stop casting <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to 2 minutes when you receive many notifications in a short time span."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organisation owns this device and may monitor network traffic"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Touch the fingerprint sensor. It\'s the shorter button on the side of the phone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using 3 fingers anywhere on the touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 303e922..7c6262c 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"You will stop recording <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"You will stop sharing <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"You will stop casting <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to 2 minutes when you receive many notifications in a short time span."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organisation owns this device and may monitor network traffic"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Touch the fingerprint sensor. It\'s the shorter button on the side of the phone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using 3 fingers anywhere on the touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 5a5af96..b4fe1ec 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -125,26 +125,19 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
- <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
- <skip />
- <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
- <skip />
- <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
- <skip />
- <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
- <skip />
- <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
- <skip />
- <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
- <skip />
- <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
- <skip />
- <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
- <skip />
- <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
- <skip />
- <!-- no translation found for close_dialog_button (4749497706540104133) -->
- <skip />
+ <string name="screenrecord_stop_dialog_title" msgid="2685522129492260887">"Stop recording screen?"</string>
+ <string name="screenrecord_stop_dialog_message" msgid="1926783607059442889">"You will stop recording your screen"</string>
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"You will stop recording <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
+ <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
+ <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+ <string name="share_to_app_stop_dialog_message" msgid="3181723638915877339">"You will stop sharing your screen"</string>
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"You will stop sharing <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
+ <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
+ <string name="cast_to_other_device_stop_dialog_title" msgid="1910372600290258193">"Stop casting screen?"</string>
+ <string name="cast_to_other_device_stop_dialog_message" msgid="1502520537030715412">"You will stop casting your screen"</string>
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"You will stop casting <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
+ <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stop casting"</string>
+ <string name="close_dialog_button" msgid="4749497706540104133">"Close"</string>
<string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
<string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
<string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
@@ -155,8 +148,7 @@
<string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
<string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
- <!-- no translation found for immersive_cling_description (2717426731830851921) -->
- <skip />
+ <string name="immersive_cling_description" msgid="2717426731830851921">"To exit, swipe down from the top of your screen"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
<string name="accessibility_back" msgid="6530104400086152611">"Back"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -299,8 +291,7 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
- <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
- <skip />
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Automatically turn on tomorrow"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
<string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
@@ -487,6 +478,9 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"select widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remove widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"place selected widget"</string>
+ <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Lock screen widgets"</string>
+ <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"To open an app using a widget, you’ll need to verify it’s you. Also, keep in mind that anyone can view them, even when your tablet’s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here."</string>
+ <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Got it"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Switch user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
@@ -540,6 +534,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptive notifications is on"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Your device now lowers the volume and reduces pop-ups on the screen for up to two minutes when you receive many notifications in a short time span."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"This device is managed by your parent"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Your organization owns this device and may monitor network traffic"</string>
@@ -1195,6 +1192,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Touch the fingerprint sensor. It’s the shorter button on the side of the phone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1345,14 +1343,14 @@
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
<string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
- <!-- no translation found for touchpad_tutorial_back_gesture_button (2746834288077265946) -->
- <skip />
- <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
- <skip />
- <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
- <skip />
- <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
- <skip />
+ <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
+ <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
+ <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
+ <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"To go back, swipe left or right using three fingers anywhere on the touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing three fingers moving right and left"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index ce5bd5e..b17e8dc 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Dejarás de grabar contenido de <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Dejarás de compartir contenido de <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Dejarás de transmitir contenido de <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana a la mañana"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"Seleccionar widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"quitar widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar widget seleccionado"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú expandible"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Comenzar ahora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No hay notificaciones"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No hay notificaciones nuevas"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver notificaciones anteriores"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Tu padre o madre administra este dispositivo"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Tu organización es propietaria de este dispositivo y podría controlar el tráfico de red"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Presiona para obtener más información"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No establecida"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ingresa el bloqueo pantalla"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toca el sensor de huellas dactilares. Es el botón más corto del lateral del teléfono"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de huellas dactilares"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ingresar al dispositivo"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 000db75..01ea097 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Dejarás de grabar <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Dejarás de compartir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Dejarás de enviar <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana por la mañana"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -387,7 +388,7 @@
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string>
<string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
<string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
- <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+ <string name="thermal" msgid="6758074791325414831">"Temperatura"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"seleccionar widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"eliminar widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar widget seleccionado"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú desplegable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Empezar ahora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"No hay notificaciones"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"No hay notificaciones nuevas"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver notificaciones anteriores"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo lo gestionan tu padre o tu madre"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"El dispositivo pertenece a tu organización, que puede monitorizar su tráfico de red"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca la pantalla para consultar más información"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ninguna puesta"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"Poner bloqueo de pantalla"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toca el sensor de huellas digitales. Es el botón más pequeño del lateral del teléfono."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de huellas digitales"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticarte"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acceder al dispositivo"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index fa10a8a..b118040 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Lõpetate rakenduse <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> sisu salvestamise"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Lõpetate rakenduse <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> sisu jagamise"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Lõpetate rakenduse <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> sisu ülekandmise"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Sellised funktsioonid nagu Kiirjagamine ja Leia mu seade kasutavad Bluetoothi."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Jaga heli"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Heli jagamine"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidina valimine"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"eemaldage vidin"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"asetage valitud vidin"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kasutaja vahetamine"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rippmenüü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Alusta kohe"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Märguandeid pole"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Uusi märguandeid ei ole"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Kohanduvad märguanded on sees"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Seade vähendab nüüd helitugevust ja ekraanil kuvatavaid hüpikaknaid kuni kaheks minutiks, kui saate lühikese aja jooksul palju märguandeid."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Lülita välja"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Vanemate märguannete nägemiseks avage"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Seda seadet haldab sinu vanem"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Teie organisatsioon on selle seadme omanik ja võib jälgida võrguliiklust"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Puudutage lisateabe saamiseks"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Äratust pole"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"sisesta ekraanilukk"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Puudutage sõrmejäljeandurit. See on lühem nupp telefoni küljel."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sõrmejäljeandur"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentimiseks"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"seadmesse sisenemiseks"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Tagasiliikumiseks pühkige puuteplaadil kolme sõrmega vasakule või paremale."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Puuteplaat kolme paremale ja vasakule liikuva sõrmega"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Seadme ekraanil näidatakse tagasiliigutuse animatsiooni"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 960a92f..d717b21 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> grabatzeari utziko diozu"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> partekatzeari utziko diozu"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> igortzeari utziko diozu"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Bilatu nire gailua eta beste eginbide batzuek Bluetootha erabiltzen dute"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bihar goizean aktibatuko da Bluetootha"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partekatu audioa"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioa partekatzen"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"hautatu widget bat"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"kendu widgeta"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"kokatu hautatutako widgeta"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"zabaldu menua"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Hasi"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ez dago jakinarazpenik"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ez dago jakinarazpen berririk"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Jakinarazpen zaharragoak ikusteko, desblokeatu"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Zure gurasoak kudeatzen du gailua"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Gailu hau zure erakundearena da, eta baliteke hark sareko trafikoa gainbegiratzea"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Informazio gehiago lortzeko, sakatu hau"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarmarik ez"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"erabili pantailaren blokeoa"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Ukitu hatz-marken sentsorea. Pantailaren albo batean dagoen botoi laburra da."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Hatz-marken sentsorea"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatu"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"sartu gailuan"</string>
@@ -1320,7 +1334,7 @@
<string name="dismiss_dialog" msgid="2195508495854675882">"Baztertu"</string>
<string name="connected_display_icon_desc" msgid="6373560639989971997">"Konektatutako pantaila"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofonoa eta kamera"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"Aplikazioen azken erabilera"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"Aplikazioen azkenaldiko erabilera"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ikusi azkenaldiko sarbidea"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Eginda"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Zabaldu eta erakutsi aukerak"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f02c248..e690f3d 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -122,25 +122,28 @@
<string name="screenrecord_stop_label" msgid="72699670052087989">"متوقف کردن"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"همرسانی"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"قطعه ضبطشده از صفحهنمایش ذخیره شد"</string>
- <string name="screenrecord_save_text" msgid="3008973099800840163">"برای مشاهده ضربه بزنید"</string>
+ <string name="screenrecord_save_text" msgid="3008973099800840163">"برای مشاهده تکضرب بزنید"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"خطا در ذخیرهسازی ضبط صفحهنمایش"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"خطا هنگام شروع ضبط صفحهنمایش"</string>
<!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"ضبط <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> را متوقف خواهید کرد"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"همرسانی <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> را متوقف خواهید کرد"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"پخش محتوای <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> را متوقف خواهید کرد"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -151,7 +154,7 @@
<string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"درحال ضبط کردن مشکل"</string>
<string name="issuerecord_share_label" msgid="3992657993619876199">"همرسانی"</string>
<string name="issuerecord_save_title" msgid="4161043023696751591">"ضبط مشکل ذخیره شد"</string>
- <string name="issuerecord_save_text" msgid="1205985304551521495">"برای مشاهده ضربه بزنید"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"برای مشاهده تکضرب بزنید"</string>
<string name="issuerecord_save_error" msgid="6913040083446722726">"هنگام ذخیره کردن ضبط مشکل، خطایی پیش آمد"</string>
<string name="issuerecord_start_error" msgid="3402782952722871190">"هنگام شروع ضبط مشکل، خطایی پیش آمد"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"درحال مشاهده در حالت تمامصفحه"</string>
@@ -179,12 +182,12 @@
</string-array>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"تأیید"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"امتحان مجدد"</string>
- <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"برای لغو راستیآزمایی ضربه بزنید"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"برای لغو راستیآزمایی تکضرب بزنید"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"لطفاً دوباره امتحان کنید"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"درحال جستجوی چهره"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره اصالتسنجی شد"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تأیید شد"</string>
- <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید ضربه بزنید"</string>
+ <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید تکضرب بزنید"</string>
<string name="biometric_dialog_tap_confirm_with_face" msgid="2378151312221818694">"قفل با چهره باز شد"</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"قفلْ با چهره باز شد. برای ادامه، فشار دهید."</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"چهره شناسایی شد. برای ادامه، فشار دهید."</string>
@@ -290,7 +293,7 @@
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"مزاحم نشوید"</string>
<string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"هیچ دستگاه مرتبط شدهای موجود نیست"</string>
- <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"برای اتصال یا قطع اتصال دستگاه، ضربه بزنید"</string>
+ <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"برای اتصال یا قطع اتصال دستگاه، تکضرب بزنید"</string>
<string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"جفت کردن دستگاه جدید"</string>
<string name="see_all_bluetooth_devices" msgid="1761596816620200433">"دیدن همه"</string>
<string name="turn_on_bluetooth" msgid="5681370462180289071">"استفاده از بلوتوث"</string>
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ویژگیهایی مثل «همرسانی سریع» و «پیدا کردن دستگاهم» از بلوتوث استفاده میکنند"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"همرسانی صدا"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"درحال همرسانی صدا"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -431,8 +432,8 @@
<string name="zen_priority_customize_button" msgid="4119213187257195047">"سفارشی کردن"</string>
<string name="zen_silence_introduction_voice" msgid="853573681302712348">"این کار «همه» صداها و لرزشها (شامل هشدار، موسیقی، ویدیو و بازی) را مسدود میکند. همچنان میتوانید تماس تلفنی برقرار کنید."</string>
<string name="zen_silence_introduction" msgid="6117517737057344014">"این کار «همه» صداها و لرزشها از جمله هشدارها، موسیقی، ویدئوها و بازیها را مسدود میکند."</string>
- <string name="notification_tap_again" msgid="4477318164947497249">"دوباره ضربه بزنید تا باز شود"</string>
- <string name="tap_again" msgid="1315420114387908655">"دوباره ضربه بزنید"</string>
+ <string name="notification_tap_again" msgid="4477318164947497249">"دوباره تکضرب بزنید تا باز شود"</string>
+ <string name="tap_again" msgid="1315420114387908655">"دوباره تکضرب بزنید"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند بهبالا بکشید"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"برای باز کردن، نماد قفلگشایی را فشار دهید"</string>
<string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"قفلْ با چهره باز شد. برای باز کردن، تند بهبالا بکشید."</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"انتخاب ابزارک"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"برداشتن ابزارک"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"جایگذاری ابزارک انتخابشده"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایینپر"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"اکنون شروع کنید"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"اعلانی موجود نیست"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"اعلان جدیدی وجود ندارد"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"اعلانهای تطبیقی روشن است"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ازاینپس هروقت اعلانهای زیادی در یک بازه زمانی کوتاه دریافت کنید، دستگاهتان تا دو دقیقه صدا را کم میکند و تعداد پنجرههای بالاپر را در صفحهنمایش کاهش میدهد."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"خاموش کردن"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"برای دیدن اعلانهای قبلی قفل را باز کنید"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"این دستگاه را ولیتان مدیریت میکند"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"مالک این دستگاه سازمان شما است و ممکن است ترافیک شبکه را پایش کند"</string>
@@ -634,17 +644,17 @@
<string name="stream_notification_unavailable" msgid="4313854556205836435">"دردسترس نیست، چون زنگ بیصدا شده است"</string>
<string name="stream_alarm_unavailable" msgid="4059817189292197839">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string>
<string name="stream_media_unavailable" msgid="6823020894438959853">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string>
- <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. برای باصدا کردن ضربه بزنید."</string>
- <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. برای تنظیم روی لرزش ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
- <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. برای صامت کردن ضربه بزنید. ممکن است سرویسهای دسترسپذیری صامت شود."</string>
- <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. برای تنظیم روی لرزش، ضربه بزنید."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. برای صامت کردن ضربه بزنید."</string>
+ <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. برای باصدا کردن تکضرب بزنید."</string>
+ <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. برای تنظیم روی لرزش تکضرب بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
+ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. برای صامت کردن تکضرب بزنید. ممکن است سرویسهای دسترسپذیری صامت شود."</string>
+ <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. برای تنظیم روی لرزش، تکضرب بزنید."</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. برای صامت کردن تکضرب بزنید."</string>
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"کنترل صدای محیط"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"صدای فضایی"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"خاموش"</string>
<string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"ثابت"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ردیابی سر"</string>
- <string name="volume_ringer_change" msgid="3574969197796055532">"برای تغییر حالت زنگ، ضربه بزنید"</string>
+ <string name="volume_ringer_change" msgid="3574969197796055532">"برای تغییر حالت زنگ، تکضرب بزنید"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string>
<string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string>
@@ -670,7 +680,7 @@
<string name="wallet_title" msgid="5369767670735827105">"کیف پول"</string>
<string name="wallet_empty_state_label" msgid="7776761245237530394">"برای خرید سریعتر و امنتر با تلفن، راهاندازی کنید"</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"نمایش همه"</string>
- <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"برای باز کردن ضربه بزنید"</string>
+ <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"برای باز کردن تکضرب بزنید"</string>
<string name="wallet_secondary_label_updating" msgid="5726130686114928551">"درحال بهروزرسانی"</string>
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارتها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
@@ -936,7 +946,7 @@
<string name="instant_apps" msgid="8337185853050247304">"برنامههای فوری"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> درحال اجرا"</string>
<string name="instant_apps_message" msgid="6112428971833011754">"برنامه بدون نصب شدن باز شد."</string>
- <string name="instant_apps_message_with_help" msgid="1816952263531203932">"برنامه بدون نصب شدن باز شد. برای اطلاعات بیشتر ضربه بزنید."</string>
+ <string name="instant_apps_message_with_help" msgid="1816952263531203932">"برنامه بدون نصب شدن باز شد. برای اطلاعات بیشتر تکضرب بزنید."</string>
<string name="app_info" msgid="5153758994129963243">"اطلاعات برنامه"</string>
<string name="go_to_web" msgid="636673528981366511">"رفتن به مرورگر"</string>
<string name="mobile_data" msgid="4564407557775397216">"داده تلفن همراه"</string>
@@ -950,7 +960,7 @@
<string name="qs_dnd_prompt_app" msgid="4027984447935396820">"برنامهای (<xliff:g id="ID_1">%s</xliff:g>) «مزاحم نشوید» را روشن کرد."</string>
<string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"برنامه یا قانون خودکاری، «مزاحم نشوید» را روشن کرد."</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"برنامههایی که در پسزمینه اجرا میشوند"</string>
- <string name="running_foreground_services_msg" msgid="3009459259222695385">"برای جزئیات مربوط به مصرف باتری و داده، ضربه بزنید"</string>
+ <string name="running_foreground_services_msg" msgid="3009459259222695385">"برای جزئیات مربوط به مصرف باتری و داده، تکضرب بزنید"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"داده تلفن همراه خاموش شود؟"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"نمیتوانید ازطریق <xliff:g id="CARRIER">%s</xliff:g> به داده یا اینترنت دسترسی داشته باشید. اینترنت فقط ازطریق Wi-Fi در دسترس خواهد بود."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"شرکت مخابراتی شما"</string>
@@ -965,7 +975,7 @@
<string name="slice_permission_checkbox" msgid="4242888137592298523">"به <xliff:g id="APP">%1$s</xliff:g> اجازه داده شود تکههایی از برنامهها نشان دهد"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"اجازه دادن"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"مجاز نبودن"</string>
- <string name="auto_saver_title" msgid="6873691178754086596">"برای زمانبندی «بهینهسازی باتری» ضربه بزنید"</string>
+ <string name="auto_saver_title" msgid="6873691178754086596">"برای زمانبندی «بهینهسازی باتری» تکضرب بزنید"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبهاتمام است، بهینهسازی باتری را روشن کنید"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"استفاده شده"</string>
@@ -1025,11 +1035,11 @@
<string name="accessibility_magnification_done" msgid="263349129937348512">"تمام"</string>
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"ویرایش"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"تنظیمات پنجره ذرهبین"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"برای باز کردن ویژگیهای دسترسپذیری ضربه بزنید. در تنظیمات این دکمه را سفارشی یا جایگزین کنید\n\n"<annotation id="link">"تنظیمات"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"برای باز کردن ویژگیهای دسترسپذیری تکضرب بزنید. در تنظیمات این دکمه را سفارشی یا جایگزین کنید\n\n"<annotation id="link">"تنظیمات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"برای پنهان کردن موقتی دکمه، آن را به لبه ببرید"</string>
<string name="accessibility_floating_button_undo" msgid="511112888715708241">"واگرد"</string>
<string name="accessibility_floating_button_hidden_notification_title" msgid="4115036997406994799">"دکمه دسترسپذیری پنهان شده است"</string>
- <string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"برای نمایش دکمه دسترسپذیری ضربه بزنید"</string>
+ <string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"برای نمایش دکمه دسترسپذیری تکضرب بزنید"</string>
<string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"میانبر «<xliff:g id="FEATURE_NAME">%s</xliff:g>» برداشته شد"</string>
<string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{میانبر «#» برداشته شد}one{میانبر «#» برداشته شد}other{میانبر «#» برداشته شد}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"انتقال به بالا سمت راست"</string>
@@ -1130,7 +1140,7 @@
<string name="media_output_dialog_single_device" msgid="3102758980643351058">"۱ دستگاه انتخاب شد"</string>
<string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> دستگاه انتخاب شد"</string>
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(اتصال قطع شد)"</string>
- <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"عوض نمیشود. برای تلاش مجدد ضربه بزنید."</string>
+ <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"عوض نمیشود. برای تلاش مجدد تکضرب بزنید."</string>
<string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"متصل کردن دستگاه"</string>
<string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"برای ارسال محتوای این جلسه، لطفاً برنامه را باز کنید."</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"برنامه ناشناس"</string>
@@ -1159,7 +1169,7 @@
<string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریدهدان کپی شد."</string>
<string name="basic_status" msgid="2315371112182658176">"باز کردن مکالمه"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"ابزارکهای مکالمه"</string>
- <string name="select_conversation_text" msgid="3376048251434956013">"روی مکالمهای ضربه بزنید تا به «صفحه اصلی» اضافه شود"</string>
+ <string name="select_conversation_text" msgid="3376048251434956013">"روی مکالمهای تکضرب بزنید تا به «صفحه اصلی» اضافه شود"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"آخرین مکالمههای شما اینجا نشان داده میشود"</string>
<string name="priority_conversations" msgid="3967482288896653039">"مکالمههای اولویتدار"</string>
<string name="recent_conversations" msgid="8531874684782574622">"گفتگوهای اخیر"</string>
@@ -1194,9 +1204,10 @@
<string name="new_status_content_description" msgid="6046637888641308327">"<xliff:g id="NAME">%1$s</xliff:g> وضعیتش را بهروزرسانی کرد: <xliff:g id="STATUS">%2$s</xliff:g>"</string>
<string name="person_available" msgid="2318599327472755472">"دردسترس"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"مشکلی در خواندن میزان باتری وجود دارد"</string>
- <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"برای اطلاعات بیشتر ضربه بزنید"</string>
+ <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"برای اطلاعات بیشتر تکضرب بزنید"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"هشداری تنظیم نشده"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"وارد کردن قفل صفحه"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"حسگر اثر انگشت را لمس کنید. این حسگر همان دکمه کوچکتر در کنار تلفن است"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"حسگر اثرانگشت"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"اصالتسنجی کردن"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"وارد شدن به دستگاه"</string>
@@ -1213,7 +1224,7 @@
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"شبکه دیگری وجود ندارد"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"شبکهای در دسترس نیست"</string>
<string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
- <string name="tap_a_network_to_connect" msgid="1565073330852369558">"برای اتصال به شبکه روی آن ضربه بزنید"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"برای اتصال به شبکه روی آن تکضرب بزنید"</string>
<string name="unlock_to_view_networks" msgid="5072880496312015676">"برای مشاهده شبکهها، قفل صفحه را باز کنید"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string>
@@ -1239,7 +1250,7 @@
<string name="clipboard_edit_text_description" msgid="805254383912962103">"ویرایش نوشتار کپیشده"</string>
<string name="clipboard_edit_image_description" msgid="8904857948976041306">"ویرایش تصویر کپیشده"</string>
<string name="clipboard_send_nearby_description" msgid="4629769637846717650">"ارسال به دستگاهی در اطراف"</string>
- <string name="clipboard_text_hidden" msgid="7926899867471812305">"برای مشاهده، ضربه بزنید"</string>
+ <string name="clipboard_text_hidden" msgid="7926899867471812305">"برای مشاهده، تکضرب بزنید"</string>
<string name="clipboard_text_copied" msgid="5100836834278976679">"نوشتار کپی شد"</string>
<string name="clipboard_image_copied" msgid="3793365360174328722">"تصویر کپی شد"</string>
<string name="clipboard_content_copied" msgid="144452398567828145">"محتوا کپی شد"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"برگشتن"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"برای برگشتن، در هر جایی از صفحه لمسی، با سه انگشت تند بهچپ یا راست بکشید."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"صفحه لمسی که سه انگشت را درحال حرکت بهسمت راست و چپ نشان میدهد"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"صفحهنمایش دستگاه درحال نمایش پویانمایی مربوط به اشاره برگشتن"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پسزمینه صفحهکلید"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"سطح %1$d از %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 9a24082..1c508c8 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ei enää tallenna sisältöä"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ei enää jaa sisältöä"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ei enää striimaa sisältöä"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Paikanna laite ja tietyt muut ominaisuudet käyttävät Bluetoothia"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth menee päälle huomisaamuna"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Jaa audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audiota jaetaan"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"valitse widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"poista widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"aseta valittu widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Vaihda käyttäjää"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"alasvetovalikko"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Aloita nyt"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ei ilmoituksia"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ei uusia ilmoituksia"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Avaa lukitus niin näet ilmoituksia"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Vanhempasi ylläpitää tätä laitetta"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisaatiosi omistaa laitteen ja voi valvoa verkkoliikennettä"</string>
@@ -814,8 +827,8 @@
<string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Syöttötapa"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"Vaihda seuraavaan kieleen"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"Vaihda aiempaan kieleen"</string>
- <string name="input_access_emoji" msgid="8105642858900406351">"Emojien käyttö"</string>
- <string name="input_access_voice_typing" msgid="7291201476395326141">"Puhekirjoituksen käyttö"</string>
+ <string name="input_access_emoji" msgid="8105642858900406351">"Käytä emojeja"</string>
+ <string name="input_access_voice_typing" msgid="7291201476395326141">"Käytä puhekirjoitusta"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Sovellukset"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Assistant"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Selain"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Saat lisätietoja napauttamalla"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ei herätyksiä"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"käytä näytön lukitustapaa"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Kosketa sormenjälkitunnistinta. Se on puhelimen sivussa oleva lyhyempi painike."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sormenjälkitunnistin"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"todentaaksesi"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"avataksesi laitteen"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 77335de..0d93e99 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Vous arrêterez d\'enregistrer <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Vous arrêterez de partager <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Vous arrêterez de diffuser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partager l\'audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Partage de l\'audio en cours…"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"sélectionner le widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"retirer le widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placer le widget sélectionné"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverr. pour voir les anciennes notif."</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Cet appareil est géré par ton parent"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Votre organisation possède cet appareil et peut contrôler le trafic réseau"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Touchez pour en savoir plus"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Aucune alarme définie"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"entrer verrouillage de l\'écran"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toucher le capteur d\'empreintes digitales. Il s\'agit du bouton le plus court sur le côté du téléphone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Capteur d\'empreintes digitales"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"authentifier"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 98bfc72..afad406 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Vous arrêterez d\'enregistrer <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Vous arrêterez de partager <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Vous arrêterez de caster <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Certaines fonctionnalités, telles que Quick Share et Localiser mon appareil, utilisent le Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth sera activé demain matin"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partager le contenu audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio partagé"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string>
@@ -379,7 +380,7 @@
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
<string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string>
- <string name="qs_record_issue_start" msgid="2979831312582567056">"Début"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Lancer"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bug"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"sélectionner un widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"supprimer le widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"positionner le widget sélectionné"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverrouiller pour voir anciennes notifications"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Cet appareil est géré par tes parents"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Cet appareil appartient à votre organisation, qui peut contrôler votre trafic réseau"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Appuyer pour en savoir plus"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Pas d\'alarme définie"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"accéder au verrouillage de l\'écran"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Appuyez sur le lecteur d\'empreinte digitale. Il s\'agit du plus petit bouton sur le côté du téléphone."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Lecteur d\'empreinte digitale"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"s\'authentifier"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 28cf92a..5807602 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Deixarás de gravar a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Deixarás de compartir a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Deixarás de emitir a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcións como Quick Share e Localizar o meu dispositivo utilizan o Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartindo audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"seleccionar widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"quitar o widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"colocar o widget seleccionado"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menú despregable"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Non hai notificacións"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Non hai notificacións novas"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificacións intelix. activas"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O dispositivo baixa o volume e reduce as ventás emerxentes na pantalla durante 2 min como máximo cando recibes moitas notificacións en pouco tempo."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver máis notificacións"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"O teu pai ou nai xestiona este dispositivo"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"A túa organización é propietaria deste dispositivo e pode controlar o tráfico de rede"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca para obter máis información"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Sen alarmas postas"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"introducir o bloqueo de pantalla"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toca o sensor de impresión dixital. É o botón máis pequeno no lateral do teléfono"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impresión dixital"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"poñer o dispositivo"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para volver, pasa tres dedos cara á esquerda ou cara á dereita en calquera lugar do panel táctil."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Panel táctil que mostra tres dedos movéndose á dereita e á esquerda"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla do dispositivo que mostra unha animación do xesto de retroceso"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 2914bad..18107cc 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"તમે <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને રેકોર્ડ કરવાનું બંધ કરશો"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"તમે <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને શેર કરવાનું બંધ કરશો"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"તમે <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને કાસ્ટ કરવાનું બંધ કરશો"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ક્વિક શેર અને Find My Device જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ઑડિયો શેર કરો"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ઑડિયો શેર કરી રહ્યાં છીએ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"વિજેટ પસંદ કરો"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"વિજેટ કાઢી નાખો"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"પસંદ કરેલું વિજેટ મૂકો"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"વપરાશકર્તા સ્વિચ કરો"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"પુલડાઉન મેનૂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ અને ડેટા કાઢી નાખવામાં આવશે."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"હવે શરૂ કરો"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"કોઈ નોટિફિકેશન નથી"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"કોઈ નવું નોટિફિકેશન નથી"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"અડૅપ્ટિવ નોટિફિકેશન ચાલુ છે"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"તમને ટૂંકા સમયગાળામાં ઘણાં નોટિફિકેશન મળે, ત્યારે ડિવાઇસ બે મિનિટ સુધી વૉલ્યૂમ ઓછું કરે છે અને સ્ક્રીન પરના પૉપ-અપની સંખ્યા ઓછી કરે છે."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"બંધ કરો"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"જૂના નોટિફિકેશન જોવા માટે અનલૉક કરો"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"આ ડિવાઇસ તમારા માતાપિતા દ્વારા મેનેજ કરવામાં આવે છે"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"તમારી સંસ્થા આ ડિવાઇસની માલિકી ધરાવે છે અને નેટવર્ક ટ્રાફિકનું નિરીક્ષણ કરી શકે છે"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"વધુ માહિતી માટે ટૅપ કરો"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"કોઈ અલાર્મ સેટ નથી"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"સ્ક્રીન લૉક દાખલ કરો"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ફિંગરપ્રિન્ટ સેન્સરને ટચ કરો. ફોનની બાજુમાં આવેલું આ કોઈ નાનું બટન છે"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ફિંગરપ્રિન્ટ સેન્સર"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ખાતરી કરો"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ડિવાઇસ અનલૉક કરો"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"પાછા જાઓ"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"પાછા જવા માટે, ત્રણ આંગળીઓનો ઉપયોગ કરીને ટચપૅડ પર કોઈપણ જગ્યાએ ડાબે કે જમણે સ્વાઇપ કરો."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"જમણી અને ડાબી તરફ ખસી રહેલી ત્રણ આંગળીઓ બતાવતું ટચપૅડ"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"પાછા જવા માટેના સંકેત માટેનું ઍનિમેશન બતાવતી ડિવાઇસ સ્ક્રીન"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index a6bc9b5..2e0864d 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"इससे <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> का कॉन्टेंट रिकॉर्ड होना बंद हो जाएगा"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"इससे <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> का कॉन्टेंट शेयर होना बंद हो जाएगा"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"इससे <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> का कॉन्टेंट कास्ट होना बंद हो जाएगा"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेयर और Find My Device जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडियो शेयर करें"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडियो शेयर किया जा रहा है"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -382,7 +383,7 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"शुरू करें"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"रोकें"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"गड़बड़ी की रिपोर्ट"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की कौनसी सुविधा पर असर पड़ा था?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की किस सुविधा में समस्या आ रही थी?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्या का टाइप चुनें"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रिकॉर्डर"</string>
<string name="performance" msgid="6552785217174378320">"परफ़ॉर्मेंस"</string>
@@ -393,7 +394,7 @@
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ऐक्टिव"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिसकनेक्ट हो गया"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"कान की मशीनें"</string>
- <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नया डिवाइस जोड़ें"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"कान की नई मशीन जोड़ें"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"विजेट चुनें"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"विजेट हटाएं"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"चुने गए विजेट के लिए जगह चुनें"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सेशन के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"अभी शुरू करें"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"कोई सूचना नहीं है"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"कोई नई सूचना नहीं है"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"अडैप्टिव नोटिफ़िकेशन चालू हैं"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"अब आपका डिवाइस, तुरंत कई सूचनाएं मिलने पर दो मिनट तक, इनसे होने वाली आवाज़ें कम कर सकता है और स्क्रीन पर कम पॉप-अप दिखा सकता है."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"बंद करें"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"पुरानी सूचाएं देखने के लिए अनलॉक करें"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"इस डिवाइस का प्रबंधन आपके अभिभावक करते हैं"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है. आपका संगठन, नेटवर्क के ट्रैफ़िक की निगरानी कर सकता है"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ज़्यादा जानकारी के लिए टैप करें"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"कोई अलार्म सेट नहीं है"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"स्क्रीन लॉक डालें"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"फ़िंगरप्रिंट सेंसर को छुएं. यह फ़ोन की साइड में मौजूद एक छोटा बटन होता है"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फ़िंगरप्रिंट सेंसर"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"पुष्टि करें"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"डिवाइस की होम स्क्रीन पर जाएं"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"वापस जाएं"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"वापस जाने के लिए, टचपैड पर कहीं भी तीन उंगलियों से बाईं या दाईं तरफ़ स्वाइप करें."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"टचपैड पर तीन उंगलियों को दाईं और बाईं तरफ़ ले जाया जा रहा है"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"डिवाइस की स्क्रीन पर, पिछले पेज पर जाने के लिए हाथ के जेस्चर का ऐनिमेशन दिख रहा है"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index ba82bdd..b0a4751 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Zaustavit ćete snimanje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Zaustavit ćete dijeljenje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Zaustavit ćete emitiranje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -376,11 +379,11 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje zaslona"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Početak"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavi"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježi poteškoću"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježite poteškoću"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
- <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvješće o pogrešci"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string>
+ <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvješće o programskim pogreškama"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji je dio doživljaja na uređaju to utjecalo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
<string name="performance" msgid="6552785217174378320">"Izvedba"</string>
@@ -487,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"odaberi widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ukloni widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavi odabrani widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"padajući izbornik"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Izbrisat će se sve aplikacije i podaci u ovoj sesiji."</string>
@@ -540,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Pokreni"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nema obavijesti"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavijesti"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Prilagodljive obavijesti uklj."</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Uređaj sada stišava zvuk i smanjuje broj skočnih prozora na zaslonu na 2 minute kada primite jako puno obavijesti u kratkom vremenskom razdoblju."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starije obavijesti"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Ovim uređajem upravlja tvoj roditelj"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša je organizacija vlasnik ovog uređaja i može nadzirati mrežni promet"</string>
@@ -1195,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nema nijednog alarma"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"unesite zaključavanje zaslona"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dodirnite senzor otiska prsta. To je kraća tipka s bočne strane telefona"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor otiska prsta"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificirali"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pristupili uređaju"</string>
@@ -1353,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Za povratak trima prstima prijeđite ulijevo ili udesno bilo gdje na dodirnoj podlozi."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Dodirna podloga prikazuje tri prsta koji se kreću udesno i ulijevo"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Na zaslonu uređaja prikazuje se animacija za pokret za povratak"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index bb22807..3db9309 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Le fogja állítani a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> rögzítését"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Le fogja állítani a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> megosztását"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Le fogja állítani a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> átküldését"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Egyes funkciók (például a Quick Share és a Készülékkereső) Bluetootht használnak"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"A Bluetooth holnap reggel bekapcsol"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Hang megosztása"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Hang megosztása…"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -325,7 +326,7 @@
<string name="quick_settings_user_title" msgid="8673045967216204537">"Felhasználó"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string>
- <string name="quick_settings_networks_available" msgid="1875138606855420438">"Használhatók hálózatok"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Elérhető hálózatok"</string>
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Nem használhatók hálózatok"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Nincs elérhető Wi-Fi-hálózat"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Bekapcsolás…"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"modul kiválasztása"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"modul törlése"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"kijelölt modul áthelyezése"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"lehúzható menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Indítás most"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nincs értesítés"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nincsenek új értesítések"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"A régebbiek feloldás után láthatók"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Az eszközt a szülőd felügyeli"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Az eszköz az Ön szervezetének tulajdonában van, és lehetséges, hogy a hálózati forgalmat is figyelik"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Koppintással további információkat érhet el."</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nincs ébresztés"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"képernyőzár megadása"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Érintse meg az ujjlenyomat-érzékelőt. Ez a rövidebb gomb a telefon oldalán."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Ujjlenyomat-érzékelő"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"a hitelesítéshez"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"eszköz megadásához"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 14db87a..31fd759 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Դուք կկանգնեցնեք <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածից բովանդակության տեսագրումը"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Դուք կկանգնեցնեք <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածից բովանդակության փոխանցումը"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Դուք կկանգնեցնեք <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածից բովանդակության հեռարձակումը"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիո"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Աուդիոյի փոխանցում"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ընտրել վիջեթ"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"հեռացնել վիջեթը"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"տեղադրել ընտրված վիջեթը"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Անջատել օգտվողին"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"իջնող ընտրացանկ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ծանուցումներ չկան"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Նոր ծանուցումներ չկան"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ապակողպեք՝ տեսնելու հին ծանուցումները"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Այս սարքը կառավարում է ձեր ծնողը"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ձեր կազմակերպությունը այս սարքի սեփականատերն է և կարող է վերահսկել ցանցային թրաֆիկը"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Հպեք՝ ավելին իմանալու համար"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Զարթուցիչ դրված չէ"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ապակողպել էկրանը"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Հպեք մատնահետքի սկաներին։ Այն հեռախոսի կողքի ավելի կարճ կոճակն է"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Մատնահետքի սկաներ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"նույնականացնել"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"նշել սարքը"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f9f6370..812daca 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Anda akan berhenti merekam <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Anda akan berhenti membagikan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Anda akan berhenti mentransmisikan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Fitur seperti Quick Share dan Temukan Perangkat Saya menggunakan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dinyalakan besok pagi"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bagikan audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Berbagi audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -382,7 +383,7 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"Mulai"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Berhenti"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Bug"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hal apa yang terpengaruh?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string>
<string name="performance" msgid="6552785217174378320">"Performa"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pilih widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"hapus widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"letakkan widget yang dipilih"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Beralih pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pulldown"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data dalam sesi ini akan dihapus."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulai sekarang"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Tidak ada notifikasi"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Tidak ada notifikasi baru"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notifikasi adaptif aktif"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Perangkat Anda kini akan menurunkan volume dan mengurangi jendela pop-up di layar hingga dua menit saat Anda menerima banyak notifikasi dalam waktu singkat."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Nonaktifkan"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat notifikasi lama"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Perangkat ini dikelola oleh orang tuamu"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisasi Anda memiliki perangkat ini dan mungkin memantau traffic jaringan"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ketuk untuk informasi selengkapnya"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarm tidak disetel"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"masukkan kunci layar"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Sentuh sensor sidik jari. Sensor terletak di tombol yang lebih pendek di samping layar ponsel"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor sidik jari"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentikasi"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"masukkan perangkat"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Untuk kembali, geser ke kiri atau ke kanan menggunakan tiga jari ke mana saja di touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad menampilkan tiga jari yang bergerak ke kanan dan ke kiri"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Layar perangkat menampilkan animasi untuk gestur kembali"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 0266cb2..443969d 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Þú munt hætta upptöku úr <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Þú munt hætta að deila úr <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Þú munt hætta vörpun úr <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Eiginleikar eins og Flýtideiling og Finna tækið mitt nota Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Kveikt verður á Bluetooth í fyrramálið"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deila hljóði"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deilir hljóði"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"velja græju"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjarlægja græju"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"koma valinni græju fyrir"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"Fellivalmynd"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Byrja núna"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Engar tilkynningar"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Engar nýjar tilkynningar"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Taktu úr lás til að sjá eldri tilkynningar"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Foreldri þitt stjórnar þessu tæki"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Fyrirtækið þitt á þetta tæki og fylgist hugsanlega með netumferð"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ýttu til að fá frekari upplýsingar"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Enginn vekjari"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"sláðu inn skjálás"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Snertu fingrafaralesarann. Það er styttri hnappurinn á hlið símans"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingrafaralesari"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"auðkenna"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"opna tæki"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9cc5124..092cccc 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Interromperai la registrazione di <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Interromperai la condivisione di <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Interromperai la trasmissione di <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -487,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"seleziona widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"rimuovi widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posiziona il widget selezionato"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu a discesa"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
@@ -540,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Avvia adesso"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nessuna notifica"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nessuna nuova notifica"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Sblocca per vedere le notifiche meno recenti"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Questo dispositivo è gestito dai tuoi genitori"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Questo dispositivo appartiene alla tua organizzazione, che potrebbe monitorare il traffico di rete"</string>
@@ -1195,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tocca per ulteriori informazioni"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nessuna"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"inserisci blocco schermo"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Tocca il sensore di impronte digitali. È il tasto più corto sul lato dello smartphone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensore di impronte digitali"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"effettuare l\'autenticazione"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"accedere al dispositivo"</string>
@@ -1353,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 734e7da..36eb97e 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -107,7 +107,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"מתבצע עיבוד של הקלטת מסך"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"התראה מתמשכת לסשן הקלטת מסך"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"להתחיל את ההקלטה?"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"בזמן ההקלטה, תהיה ל-Android גישה לכל מה שמופיע במסך שלך או מנוגן במכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"בזמן ההקלטה, תהיה ל-Android גישה לכל מה שמופיע במסך שלך או מופעל במכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"בזמן הקלטה של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"התחלת ההקלטה"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"הקלטת אודיו"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"ההקלטה של <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> תיפסק"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"השיתוף של <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> יופסק"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"הפעלת ה-Cast של <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> תיפסק"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"תכונות כמו \'שיתוף מהיר\' ו\'איפה המכשיר שלי\' משתמשות ב-Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"חיבור ה-Bluetooth יופעל מחר בבוקר"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"שיתוף האודיו"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"מתבצע שיתוף של האודיו"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"צריך לבחור ווידג\'ט"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"הסרת הווידג\'ט"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"צריך למקם את הווידג\'ט שנבחר"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"החלפת משתמש"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"תפריט במשיכה למטה"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"אין התראות"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"אין התראות חדשות"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"יש לבטל את הנעילה כדי לראות התראות ישנות"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"המכשיר הזה מנוהל על ידי ההורה שלך"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"הארגון שלך הוא הבעלים של המכשיר הזה והוא עשוי לנטר את התנועה ברשת"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"יש להקיש כדי להציג מידע נוסף"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"לא הוגדרה"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"הזנת קוד נעילת המסך"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"צריך לגעת בחיישן טביעות האצבע. זה הלחצן הקצר יותר בצד של הטלפון"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"חיישן טביעות אצבע"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"אימות"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"הזנת מכשיר"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"רמה %1$d מתוך %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 6e62972..4f839ab 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> の記録を停止します"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> の共有を停止します"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> のキャストを停止します"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share や「デバイスを探す」などの機能は Bluetooth を使用します"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"音声を共有"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"音声を共有中"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string>
@@ -383,7 +384,7 @@
<string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"バグレポート"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"デバイスのどの部分が影響を受けましたか?"</string>
- <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"問題の種類を選択する"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"問題の種類を選択"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"スクリーン レコード"</string>
<string name="performance" msgid="6552785217174378320">"パフォーマンス"</string>
<string name="user_interface" msgid="3712869377953950887">"ユーザー インターフェース"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ウィジェットを選択"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ウィジェットを削除"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"選択したウィジェットを配置"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"プルダウン メニュー"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"今すぐ開始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"通知はありません"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"新しい通知はありません"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"通知の自動調整は ON です"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"短期間に多くの通知が届いたときに最長 2 分間デバイスの音量を下げて画面上のポップアップを減らします。"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"OFF にする"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ロック解除して以前の通知を表示"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"このデバイスは保護者によって管理されています"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"これは組織が所有するデバイスで、ネットワーク トラフィックが監視されることもあります"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"タップすると詳細が表示されます"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"アラーム未設定"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"画面ロックを設定"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"指紋認証センサーに触れてください。スマートフォンの側面にある高さが低い方のボタンです。"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋認証センサー"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"認証"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"デバイスを入力"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"戻る"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"戻るには、3 本の指でタッチパッドを左右にスワイプします。"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"タッチパッドで 3 本の指を左右に動かしている様子"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"デバイスの画面で「戻る」ジェスチャーのアニメーションが表示されている様子"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 5914f12..6eb6e42 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"თქვენ შეწყვეტთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ის</b> ჩაწერას"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"თქვენ შეწყვეტთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ის</b> გაზიარებას"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"თქვენ შეწყვეტთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ის</b> ტრანსლირებას"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ისეთი ფუნქციები, როგორიცაა სწრაფი გაზიარება და ჩემი მოწყობილობის პოვნა, იყენებს Bluetooth-ს"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"აუდიოს გაზიარება"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"მიმდინარებოს აუდიოს გაზიარება"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ვიჯეტის არჩევა"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ვიჯეტის ამოშლა"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"არჩეული ვიჯეტის განთავსება"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"მომხმარებლის გადართვა"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ჩამოშლადი მენიუ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"დაწყება ახლავე"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"შეტყობინებები არ არის."</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ახალი შეტყობინებები არ არის"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"მორგებადი შეტყობინებები ჩართულია"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"მოკლე დროში ბევრი შეტყობინების მიღებისას თქვენი მოწყობილობა ახლა უკვე ამცირებს ხმას და ეკრანზე ამომხტარი ფანჯრების რაოდენობას."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"გამორთვა"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"განბლოკეთ ძველი შეტყობინებების სანახავად"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"მოწყობილობას თქვენი მშობელი მართავს"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია და მას ქსელის ტრაფიკის მონიტორინგი შეუძლია"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"შეეხეთ მეტი ინფორმაციისთვის"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"მაღვიძარა არ არის"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ეკრანის დაბლოკვის შეყვანა"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"შეეხეთ თითის ანაბეჭდის სენსორს. ის მოკლე ღილაკია ტელეფონის კიდეზე"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"თითის ანაბეჭდის სენსორი"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ავტორიზაცია"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"მოწყობილობის შეყვანა"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"უკან დაბრუნება"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"უკან დასაბრუნებლად, სენსორულ პანელზე ნებისმიერ ადგილას სამი თითის გამოყენებით გადაფურცლეთ მარცხნივ ან მარჯვნივ."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"სენსორული პანელი, რომელიც აჩვენებს მარჯვენა და მარცხენა მიმართულებით მოძრავ სამ თითს"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"მოწყობილობის ეკრანი, რომელიც აჩვენებს უკან დაბრუნების ჟესტის ანიმაციას"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"კლავიატურის შენათება"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"დონე: %1$d %2$d-დან"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"სახლის კონტროლი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 40b9149..722b58e 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -107,7 +107,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экран жазғыш бейнесін өңдеу"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Жазу басталсын ба?"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Жазу кезінде Android жүйесі экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Жазу кезінде Android жүйесі экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерге, төлем туралы мәліметке, хабарларға, фотосуреттерге, аудиоконтент пен бейнелерге сақ болыңыз."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Қолданбаны жазу кезінде Android жүйесі қолданбада көрінетін не ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Жазуды бастау"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Аудио жазу"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасының контентін жазуды тоқтатасыз."</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасының контентін бөлісуді тоқтатасыз."</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасының контентін трансляциялауды тоқтатасыз."</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share және Find My Device сияқты функциялар Bluetooth-ты пайдаланады."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудионы бөлісу"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио бөлісіліп жатыр"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -383,9 +384,9 @@
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Тоқтату"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Қате туралы есеп"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Құрылғы қызметінің қандай түріне әсер етті?"</string>
- <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Мәселе түрін таңдаңыз."</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ақау түрін таңдаңыз"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экранды жазу"</string>
- <string name="performance" msgid="6552785217174378320">"Өнімділік режимі"</string>
+ <string name="performance" msgid="6552785217174378320">"Өнімділік"</string>
<string name="user_interface" msgid="3712869377953950887">"Пайдаланушы интерфейсі"</string>
<string name="thermal" msgid="6758074791325414831">"Термовизия"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет таңдау"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетті өшіру"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"таңдалған виджетті орналастыру"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Пайдаланушыны ауыстыру"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ашылмалы мәзір"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданба мен дерек жойылады."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Қазір бастау"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Хабарландырулар жоқ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Жаңа хабарландырулар жоқ"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ескі хабарландырулар үшін құлыпты ашыңыз"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Бұл құрылғыны ата-анаңыз басқарады."</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ұйымыңыз осы құрылғыны басқарады және желі трафигін бақылауы мүмкін."</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Толығырақ ақпарат алу үшін түртіңіз."</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Оятқыш орнатылмаған."</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"экран құлпын енгізу"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Саусақ ізін оқу сканерін түртіңіз. Ол – телефонның бүйіріндегі шағын түйме."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Саусақ ізін оқу сканері"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"аутентификациялау"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"құрылғыны енгізу"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 3836d7d..733196f 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"អ្នកនឹងឈប់ថត <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"អ្នកនឹងឈប់ចែករំលែក <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"អ្នកនឹងឈប់បញ្ជូន <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"មុខងារដូចជា Quick Share និង \"រកឧបករណ៍របស់ខ្ញុំ\" ប្រើប៊្លូធូស"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ស្ដាប់សំឡេងរួមគ្នា"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ជ្រើសរើសធាតុក្រាហ្វិក"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ដកធាតុក្រាហ្វិកចេញ"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ដាក់ធាតុក្រាហ្វិកដែលបានជ្រើសរើស"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរអ្នកប្រើ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ម៉ឺនុយទាញចុះ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យទាំងអស់ក្នុងវគ្គនេះនឹងត្រូវលុប។"</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ចាប់ផ្ដើមឥឡូវ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"គ្មានការជូនដំណឹង"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"គ្មានការជូនដំណឹងថ្មីៗទេ"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ការជូនដំណឹងដែលបត់បែនត្រូវបានបើក"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ឥឡូវនេះ ឧបករណ៍របស់អ្នកបន្ថយកម្រិតសំឡេង និងកាត់បន្ថយផ្ទាំងលោតឡើងនៅលើអេក្រង់រយៈពេលរហូតដល់ពីរនាទី នៅពេលអ្នកទទួលបានការជូនដំណឹងច្រើនក្នុងរយៈពេលខ្លី។"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"បិទ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ដោះសោដើម្បីមើលការជូនដំណឹងចាស់ៗ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ឧបករណ៍នេះស្ថិតក្រោមការគ្រប់គ្រងរបស់មាតាបិតាអ្នក"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ស្ថាប័នរបស់អ្នកជាម្ចាស់ឧបករណ៍នេះ ហើយអាចនឹងតាមដានចរាចរណ៍បណ្តាញ"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ចុចដើម្បីទទួលបានព័ត៌មានបន្ថែម"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"មិនបានកំណត់ម៉ោងរោទ៍ទេ"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"បញ្ចូលព័ត៌មានផ្ទៀងផ្ទាត់សម្រាប់ការចាក់សោអេក្រង់"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ប៉ះសេនស័រចាប់ស្នាមម្រាមដៃ។ វាជាប៊ូតុងខ្លីនៅចំហៀងទូរសព្ទ"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ឧបករណ៍ចាប់ស្នាមម្រាមដៃ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ផ្ទៀងផ្ទាត់"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"បញ្ចូលឧបករណ៍"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ថយក្រោយ"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ដើម្បីថយក្រោយ សូមអូសទៅឆ្វេង ឬស្ដាំដោយប្រើម្រាមដៃបីនៅកន្លែងណាមួយនៅលើផ្ទាំងប៉ះ។"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ផ្ទាំងប៉ះដែលបង្ហាញម្រាមដៃបីដែលផ្លាស់ទីទៅស្ដាំ និងឆ្វេង"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"អេក្រង់ឧបករណ៍ដែលបង្ហាញរូបមានចលនាសម្រាប់ចលនាថយក្រោយ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រងផ្ទះ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 85d67cd..cddc752 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"ಇದರಿಂದ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ನ ಕಂಟೆಂಟ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡುವುದು ನಿಂತುಹೋಗುತ್ತದೆ"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"ಇದರಿಂದ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ನ ಕಂಟೆಂಟ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುವುದು ನಿಂತುಹೋಗುತ್ತದೆ"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"ಇದರಿಂದ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ನ ಕಂಟೆಂಟ್ ಅನ್ನು ಬಿತ್ತರಿಸುವುದು ನಿಂತುಹೋಗುತ್ತದೆ"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳಿ"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string>
@@ -382,7 +383,7 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"ನಿಲ್ಲಿಸಿ"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ಬಗ್ ವರದಿ ಮಾಡುವಿಕೆ"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ನಿಮ್ಮ ಸಾಧನದ ಯಾವ ಭಾಗದಲ್ಲಿ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
<string name="performance" msgid="6552785217174378320">"ಪರ್ಫಾರ್ಮೆನ್ಸ್"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ವಿಜೆಟ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ವಿಜೆಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ಆಯ್ಕೆಮಾಡಿದ ವಿಜೆಟ್ ಅನ್ನು ಇರಿಸಿ"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ಬಳಕೆದಾರರನ್ನು ಬದಲಿಸಿ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ಪುಲ್ಡೌನ್ ಮೆನು"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಶನ್ನಲ್ಲಿನ ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ಯಾವುದೇ ಹೊಸ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್ ಆನ್ ಇದೆ"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ಅಲ್ಪಾವಧಿಯಲ್ಲಿ ಹಲವು ನೋಟಿಫಿಕೇಶನ್ ಬಂದಾಗ 2 ನಿಮಿಷದವರೆಗೆ ಸಾಧನವು ಈಗ ವಾಲ್ಯೂಮ್ ಕಡಿಮೆ ಮಾಡುತ್ತದೆ ಹಾಗೂ ಸ್ಕ್ರೀನ್ ಮೇಲಿನ ಪಾಪ್-ಅಪ್ ಕಡಿಮೆ ಮಾಡುತ್ತದೆ."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ಆಫ್ ಮಾಡಿ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ಹಳೆಯ ಅಧಿಸೂಚನೆಗಳನ್ನು ನೋಡಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ಈ ಸಾಧನವನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನದ ಮಾಲೀಕತ್ವವನ್ನು ಹೊಂದಿದೆ ಮತ್ತು ಅದು ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ನ ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ಅಲಾರಾಂ ಸೆಟ್ ಆಗಿಲ್ಲ"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ನಮೂದಿಸಿ"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ. ಇದು ಫೋನ್ನ ಬದಿಯಲ್ಲಿರುವ ಚಿಕ್ಕ ಬಟನ್ ಆಗಿದೆ"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ದೃಢೀಕರಿಸಿ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ಸಾಧನವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ಹಿಂತಿರುಗಿ"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ಹಿಂತಿರುಗಲು, ಟಚ್ಪ್ಯಾಡ್ನಲ್ಲಿ ಎಲ್ಲಿಯಾದರೂ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಎಡ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ಮೂರು ಬೆರಳುಗಳು ಬಲಕ್ಕೆ ಮತ್ತು ಎಡಕ್ಕೆ ಚಲಿಸುತ್ತಿರುವುದನ್ನು ತೋರಿಸುತ್ತಿರುವ ಟಚ್ಪ್ಯಾಡ್"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ಗೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯನಿಮೇಶನ್ ಅನ್ನು ತೋರಿಸುತ್ತಿರುವ ಸಾಧನದ ಸ್ಕ್ರೀನ್"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್ಲೈಟ್"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 4c26ed2..c33c291 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> 녹화가 중단됩니다."</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> 공유가 중단됩니다."</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> 전송이 중단됩니다."</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, 내 기기 찾기 등의 기능에서 블루투스를 사용합니다."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"오디오 공유"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"오디오 공유 중"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"위젯 선택"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"위젯 삭제"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"선택한 위젯 배치"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"사용자 전환"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"풀다운 메뉴"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"시작하기"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"알림 없음"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"새로운 알림 없음"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"잠금 해제하여 이전 알림 보기"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"부모님이 관리하는 기기입니다."</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"내 조직에서 이 기기를 소유하며 네트워크 트래픽을 모니터링할 수 있습니다."</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"탭하여 자세한 정보를 확인하세요."</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"설정된 알람 없음"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"화면 잠금 입력"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"지문 센서를 터치하세요. 휴대전화 측면에 있는 더 짧은 버튼입니다"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"지문 센서"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"인증"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"기기 입력"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ae91a7c..9cf0e98 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосун жаздырууну токтотосуз"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосун бөлүшүүнү токтотосуз"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосун тышкы экранга чыгарууну токтотосуз"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -153,7 +156,7 @@
<string name="issuerecord_save_title" msgid="4161043023696751591">"Жаздырылган маселе сакталды"</string>
<string name="issuerecord_save_text" msgid="1205985304551521495">"Көрүү үчүн таптаңыз"</string>
<string name="issuerecord_save_error" msgid="6913040083446722726">"Жаздырылган маселе сакталган жок"</string>
- <string name="issuerecord_start_error" msgid="3402782952722871190">"Маселени жаздыруу башталбай койду"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Башталбай койду"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Толук экран режимин көрүү"</string>
<!-- no translation found for immersive_cling_description (2717426731830851921) -->
<skip />
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функцияларда колдонулат"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Чогуу угуу"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Чогуу угулууда"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -382,15 +383,15 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"Баштоо"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Токтотуу"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Мүчүлүштүк тууралуу кабарлоо"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Түзмөгүңүздүн кайсы бөлүгүнө кедергиси тийди?"</string>
- <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Маселенин түрүн тандоо"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Кандай маселеге кабылдыңыз?"</string>
+ <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Маселенин түрүн тандаңыз"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экрандан видео жаздырып алуу"</string>
<string name="performance" msgid="6552785217174378320">"Иштин майнаптуулугу"</string>
<string name="user_interface" msgid="3712869377953950887">"Колдонуучунун интерфейси"</string>
<string name="thermal" msgid="6758074791325414831">"Жылуулук"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string>
- <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Жигердүү"</string>
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Иштеп жатат"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратылды"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Угуу аппараттары"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңы түзмөк кошуу"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет тандоо"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетти алып салуу"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"тандалган виджетти жайгаштыруу"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана аларга байланыштуу нерселер өчүрүлөт."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Азыр баштоо"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Билдирме жок"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Жаңы билдирмелер жок"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Билдирмелерди көрүү үчүн кулпуну ачыңыз"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Бул түзмөктү ата-энең башкарат"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Бул түзмөк уюмуңузга таандык. Уюмуңуз тармактын трафигин көзөмөлдөй алат"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Кеңири маалымат алуу үчүн таптап коюңуз"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ойготкуч коюлган жок"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"экран кулпусун киргизүү"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Манжа изинин сенсорун басыңыз. Ал телефондун капталындагы кыска баскыч"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Манжа изинин сенсору"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"аныктыгын текшерүү"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"түзмөккө кирүү"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Үйдөгү түзмөктөрдү тескөө"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 30f8a65..7bb5041 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"ທ່ານຈະຢຸດການບັນທຶກ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"ທ່ານຈະຢຸດການແບ່ງປັນ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"ທ່ານຈະຢຸດການສົ່ງສັນຍານ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ຄຸນສົມບັດຕ່າງໆໃຊ້ Bluetooth ເຊັ່ນ: ການແຊຣ໌ດ່ວນ ແລະ ຊອກຫາອຸປະກອນຂອງຂ້ອຍ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ແບ່ງປັນສຽງ"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ກຳລັງແບ່ງປັນສຽງ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ເລືອກວິດເຈັດ"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ລຶບວິດເຈັດອອກ"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ວາງວິດເຈັດທີ່ເລືອກ"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ເມນູແບບດຶງລົງ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ເລີ່ມດຽວນີ້"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ບໍ່ມີການແຈ້ງເຕືອນ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ບໍ່ມີການແຈ້ງເຕືອນໃໝ່"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ການແຈ້ງເຕືອນແບບປັບອັດຕະໂນມັດເປີດຢູ່"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ຈາກນີ້ໄປອຸປະກອນຂອງທ່ານຈະຫຼຸດລະດັບສຽງ ແລະ ຈຳນວນປັອບອັບຢູ່ໜ້າຈໍເປັນເວລາສູງສຸດ 2 ນາທີເມື່ອທ່ານໄດ້ຮັບການແຈ້ງເຕືອນຈຳນວນຫຼາຍໃນໄລຍະເວລາສັ້ນໆ."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ປິດ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ປົດລັອກເພື່ອເບິ່ງການແຈ້ງເຕືອນເກົ່າ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ອຸປະກອນນີ້ແມ່ນຈັດການໂດຍພໍ່ແມ່ຂອງທ່ານ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ອົງການຂອງທ່ານເປັນເຈົ້າຂອງອຸປະກອນນີ້ ແລະ ສາມາດຕິດຕາມທຣາບຟິກເຄືອຂ່າຍໄດ້"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ແຕະເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ບໍ່ໄດ້ຕັ້ງໂມງປຸກ"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ໃສ່ຂໍ້ມູນການລັອກໜ້າຈໍ"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ແຕະເຊັນເຊີລາຍນິ້ວມື. ໂດຍເປັນປຸ່ມທີ່ສັ້ນກວ່າເຊິ່ງຢູ່ທາງຂ້າງໂທລະສັບ"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ເຊັນເຊີລາຍນິ້ວມື"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ພິສູດຢືນຢັນ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ເຂົ້າອຸປະກອນ"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ກັບຄືນ"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ເພື່ອກັບຄືນ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຊ້າຍ ຫຼື ຂວາບ່ອນໃດກໍໄດ້ເທິງແຜ່ນສໍາຜັດ."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ແຜ່ນສໍາຜັດສະແດງພາບ 3 ນິ້ວເລື່ອນໄປທາງຂວາ ແລະ ຊ້າຍ"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ໜ້າຈໍອຸປະກອນສະແດງພາບເຄື່ອນໄຫວຂອງທ່າທາງກັບຄືນ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ໄຟປຸ່ມແປ້ນພິມ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ລະດັບທີ %1$d ຈາກ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ການຄວບຄຸມເຮືອນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 4fffb3d..7f8da47 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Programos (<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>) turinio įrašymas bus sustabdytas."</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Programos (<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>) turinio bendrinimas bus sustabdytas."</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Programos (<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>) turinio perdavimas bus sustabdytas."</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tokioms funkcijoms kaip „Spartusis bendrinimas“ ir „Rasti įrenginį“ naudojamas „Bluetooth“ ryšys"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"„Bluetooth“ ryšys bus įjungtas rytoj ryte"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bendrinti garsą"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Bendrinamas garsas"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pasirinkite valdiklį"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"pašalinti valdiklį"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"padėti pasirinktą valdiklį"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"išplečiamasis meniu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Pradėti dabar"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nėra įspėjimų"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Naujų pranešimų nėra"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptyvieji pranešimai įjungti"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Dabar, gaunant daug pranešimų per trumpą laiką, sumažinamas įrenginio garsumas ir iššokančiųjų langų skaičius ekrane iki dviejų minučių."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Išjungti"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Atrakinę matykite senesnius pranešimus"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Šį įrenginį tvarko vienas iš tavo tėvų"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Šis įrenginys priklauso jūsų organizacijai ir ji gali stebėti tinklo srautą"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Palieskite, kad sužinotumėte daugiau informacijos"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenustatyta signalų"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"įvesti ekrano užraktą"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Palieskite piršto antspaudo jutiklį. Tai yra trumpesnis mygtukas telefono šone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Kontrolinio kodo jutiklis"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"nustatytumėte tapatybę"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"pasiektumėte įrenginį"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Grįžti"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Jei norite grįžti, trimis pirštais perbraukite kairėn arba dešinėn bet kurioje jutiklinės dalies vietoje."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Jutiklinė dalis, kurioje rodomi trys dešinėn ir kairėn judantys pirštai"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Įrenginio ekranas, kuriame rodoma grįžimo gesto animacija"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 28b15d0..7121eba 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Tiks pārtraukta lietotnes <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> satura ierakstīšana"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Tiks pārtraukta lietotnes <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> satura kopīgošana"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Tiks pārtraukta lietotnes <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> satura apraide"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tādas funkcijas kā “Ātrā kopīgošana” un “Atrast ierīci” izmanto Bluetooth savienojumu"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth savienojums tiks ieslēgts rīt no rīta"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Kopīgot audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Notiek audio kopīgošana…"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"atlasīt logrīku"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"noņemt logrīku"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"novietot atlasīto logrīku"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mainīt lietotāju"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"novelkamā izvēlne"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Sākt tūlīt"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nav paziņojumu"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nav jaunu paziņojumu"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptīvie paziņojumi ieslēgti"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Jūsu ierīce tagad līdz pat 2 minūtēm samazina skaļuma līmeni un ierobežo uznirstošo elementu skaitu, kad īsā laika posmā saņemat daudzu paziņojumu."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Izslēgt"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Atbloķējiet vecāku paziņojumu skatīšanai"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Šo ierīci pārvalda viens no jūsu vecākiem."</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Šī ierīce pieder jūsu organizācijai, un jūsu organizācija var uzraudzīt tīkla datplūsmu."</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Pieskarieties, lai iegūtu plašāku informāciju."</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nav iestatīts signāls"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ievadīt ekrāna bloķēšanas informāciju"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Pieskarieties pirksta nospieduma sensoram. Tā ir īsākā poga tālruņa malā."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Pirksta nospieduma sensors"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"veiktu autentificēšanu"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"izmantotu ierīci"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atpakaļ"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Lai pārietu atpakaļ, ar trim pirkstiem velciet pa kreisi vai pa labi jebkurā vietā uz skārienpaliktņa"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Attēls ar skārienpaliktni, uz kura trīs pirksti kustas pa labi un pa kreisi"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ierīces ekrāns, kurā redzama animācija ar žestu pāriešanai atpakaļ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index bf71f8e..18b727d 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Ќе го сопрете снимањето на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Ќе го сопрете споделувањето на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Ќе го сопрете емитувањето на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функциите како „Брзо споделување“ и „Најди го мојот уред“ користат Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Споделувај аудио"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Се споделува аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -393,7 +394,7 @@
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не е поврзано"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string>
- <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спари нов уред"</string>
+ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спарете нов уред"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"изберете виџет"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"отстранете го виџетот"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставете го избраниот виџет"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"паѓачко мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијава ќе се избришат."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни сега"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Нема известувања"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Нема нови известувања"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Отклучете за да ги видите старите известувања"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Родителот управува со уредов"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Организацијата е сопственик на уредов и може да го следи мрежниот сообраќај"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Допрете за повеќе информации"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Не е поставен аларм"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"внесете PIN/шема/лозинка"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Допрете го сензорот за отпечатоци. Тоа е пократкото копче отстрана на телефонот"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отпечатоци"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"автентицирате"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"внесете уред"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 2031af0..d075bfc8 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> റെക്കോർഡ് ചെയ്യുന്നത് നിങ്ങൾ അവസാനിപ്പിക്കും"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> പങ്കിടുന്നത് നിങ്ങൾ അവസാനിപ്പിക്കും"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> കാസ്റ്റ് ചെയ്യുന്നത് നിങ്ങൾ അവസാനിപ്പിക്കും"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ക്വിക്ക് ഷെയർ, Find My Device പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ഓഡിയോ പങ്കിടുക"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ഓഡിയോ പങ്കിടുന്നു"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"വിജറ്റ് തിരഞ്ഞെടുക്കുക"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"വിജറ്റ് നീക്കം ചെയ്യുക"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"തിരഞ്ഞെടുത്ത വിജറ്റ് നൽകുക"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ഉപയോക്താവ് മാറുക"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"പുൾഡൗൺ മെനു"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"പുതിയ അറിയിപ്പുകളൊന്നുമില്ല"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"അഡാപ്റ്റീവ് അറിയിപ്പുകൾ ഓണാണ്"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ചെറിയ കാലയളവിനുള്ളിൽ നിങ്ങൾക്ക് നിരവധി അറിയിപ്പുകൾ ലഭിക്കുമ്പോൾ ഉപകരണം ഇപ്പോൾ വോളിയം കുറയ്ക്കുകയും സ്ക്രീനിൽ പോപ്പ് അപ്പുകൾ രണ്ട് മിനിറ്റ് വരെ ചുരുക്കുകയും ചെയ്യുന്നു."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ഓഫാക്കുക"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"പഴയ അറിയിപ്പുകൾ കാണാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ഈ ഉപകരണം മാനേജ് ചെയ്യുന്നത് നിങ്ങളുടെ രക്ഷിതാവാണ്"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റെ ഉടമസ്ഥതയിലായതിനാൽ നെറ്റ്വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിച്ചേക്കാം"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"കൂടുതൽ വിവരങ്ങൾക്ക് ടാപ്പ് ചെയ്യുക"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"അലാറം സജ്ജീകരിച്ചിട്ടില്ല"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"സ്ക്രീൻ ലോക്ക് നൽകുക"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ഫിംഗർപ്രിന്റ് സെൻസർ സ്പർശിക്കുക. ഇത് ഫോണിന്റെ വശത്തുള്ള ചെറിയ ബട്ടണാണ്"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ഫിംഗർപ്രിന്റ് സെൻസർ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"പരിശോധിച്ചുറപ്പിക്കുക"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ഉപകരണം നൽകുക"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"മടങ്ങുക"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"തിരികെ പോകാൻ, ടച്ച്പാഡിൽ എവിടെയെങ്കിലും മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യുക."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"വലത്തേക്കും ഇടത്തേക്കും ചലിക്കുന്ന മൂന്ന് വിരലുകൾ കാണിക്കുന്ന ടച്ച്പാഡ്"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ബാക്ക് ജെസ്ച്ചറിനായി ആനിമേഷൻ കാണിക്കുന്ന ഉപകരണ സ്ക്രീൻ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്ലൈറ്റ്"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 556d205..899a946 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Та <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-г бичихээ болино"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Та <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-г хуваалцахаа болино"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Та <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-г дамжуулахаа болино"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд Bluetooth-г ашигладаг"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудио хуваалцах"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио хуваалцаж байна"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -378,7 +379,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Дэлгэцийн үйлдэл бичих"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Эхлүүлэх"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зогсоох"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Асуудлыг бичих"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Асуудлыг бүртгэх"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Эхлүүлэх"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Зогсоох"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Алдааны мэдээ"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виджет сонгох"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетийг хасах"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"сонгосон виджетийг байрлуулах"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"эвхмэл цэс"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Одоо эхлүүлэх"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Мэдэгдэл байхгүй"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Шинэ мэдэгдэл алга"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Хуучин мэдэгдлийг харах бол түгжээг тайл"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Энэ төхөөрөмжийг таны эцэг эх удирддаг"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Танай байгууллага энэ төхөөрөмжийг эзэмшдэг бөгөөд сүлжээний ачааллыг хянаж болно"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Нэмэлт мэдээлэл авахын тулд товшино уу"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Сэрүүлэг тавиагүй"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"дэлгэцийн түгжээ оруулах"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Хурууны хээ мэдрэгчид хүрнэ үү. Энэ нь утасны хажуу талын арай богино товчлуур юм"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Хурууны хээ мэдрэгч"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"баталгаажуулах"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"төхөөрөмж оруулах"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index a009fe1..5744634 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"तुम्ही <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> रेकॉर्ड करणे बंद कराल"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"तुम्ही <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> शेअर करणे बंद कराल"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"तुम्ही <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> कास्ट करणे बंद कराल"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेअर आणि Find My Device यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडिओ शेअर करा"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडिओ शेअर करत आहे"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"विजेट निवडा"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"विजेट काढून टाका"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"निवडलेले विजेट ठेवा"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"वापरकर्ता स्विच करा"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनू"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"आता सुरू करा"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"सूचना नाहीत"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"नवीन सूचना नाहीत"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"अडॅप्टिव्ह नोटिफिकेशन सुरू आहे"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"कमी वेळेत खूप नोटिफिकेशन मिळल्यास डिव्हाइस आता कमाल २ मिनिटे व्हॉल्यूम, स्क्रीनवरील पॉप-अप कमी करते."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"बंद करा"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"जुन्या सूचना पाहण्यासाठी अनलॉक करा"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"हे डिव्हाइस तुमच्या पालकाने व्यवस्थापित केले आहे"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"तुमच्या संस्थेकडे या डिव्हाइसची मालकी आहे आणि ती नेटवर्क ट्रॅफिकचे परीक्षण करू शकते"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"अधिक माहितीसाठी टॅप करा"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"अलार्म सेट केला नाही"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"स्क्रीन लॉक एंटर करा"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"फिंगरप्रिंट सेन्सरला स्पर्श करा. हे फोनच्या बाजूला असलेले लहान बटण आहे"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फिंगरप्रिंट सेन्सर"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ऑथेंटिकेट करा"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"डिव्हाइस एंटर करा"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"मागे जा"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"मागे जाण्यासाठी, टचपॅडवर कुठेही तीन बोटांनी डावीकडे किंवा उजवीकडे स्वाइप करा."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"तीन बोट उजवीकडे आणि डावीकडे हलताना दाखवणारे टचपॅड"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"मागील जेश्चरसाठी अॅनिमेशन दाखवणारी डिव्हाइस स्क्रीन"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 59c4071..802d145 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -107,7 +107,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Mulakan Rakaman?"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Apabila anda merakam, Android boleh mengakses apa-apa sahaja yang kelihatan pada skrin anda atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati apabila memasukkan kata laluan, butiran pembayaran, mesej, foto, audio dan video."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Semasa anda merakam, Android boleh mengakses apa-apa sahaja yang kelihatan pada skrin atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati apabila memasukkan kata laluan, butiran pembayaran, mesej, foto, audio dan video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Semasa anda merakam apl, Android boleh mengakses apa-apa sahaja yang ditunjukkan atau dimainkan pada apl itu. Oleh hal yang demikian, berhati-hati ketika memasukkan kata laluan, butiran pembayaran, mesej, foto, audio dan video."</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Mulakan rakaman"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Rakam audio"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Anda akan berhenti merakam <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Anda akan berhenti berkongsi <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Anda akan berhenti menghantar <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Ciri seperti Quick Share dan Find My Device menggunakan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Kongsi audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Perkongsian audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string>
@@ -378,11 +379,11 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rakam skrin"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodkan masalah"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodkan Masalah"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Mula"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Hentikan"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Pepijat"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Apakah pengalaman peranti yang terjejas?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string>
<string name="performance" msgid="6552785217174378320">"Prestasi"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pilih widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"alih keluar widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"letakkan widget dipilih"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Tukar pengguna"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu tarik turun"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Mulakan sekarang"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Tiada pemberitahuan"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Tiada pemberitahuan baharu"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Pemberitahuan boleh suai dihidupkan"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Kelantangan & tetingkap timbul pada skrin peranti anda dikurangkan selama dua minit apabila banyak pemberitahuan diterima dalam masa yang singkat."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Matikan"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat pemberitahuan lama"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Peranti ini diurus oleh ibu bapa anda"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisasi anda memiliki peranti ini dan mungkin memantau trafik rangkaian"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ketik untuk mendapatkan maklumat lanjut"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Tiada penggera"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"masukkan kunci skrin"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Sentuh penderia cap jari. Penderia cap jari ialah butang yang lebih pendek di sisi telefon"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Penderia cap jari"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"sahkan"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"akses peranti"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Untuk kembali, leret ke kiri atau kanan menggunakan tiga jari di mana-mana sahaja pada pad sentuh."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Pad sentuh menunjukkan tiga jari bergerak ke kanan dan kiri"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skrin peranti menunjukkan animasi untuk gerak isyarat kembali"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index d13ee63..3596430 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -106,7 +106,7 @@
<string name="screenrecord_title" msgid="4257171601439507792">"ဖန်သားပြင်ရိုက်ကူးစက်"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"စကရင်ရိုက်ကူးမှု အပြီးသတ်နေသည်"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
- <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ရိုက်သံဖမ်းခြင်း စတင်မလား။"</string>
+ <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ရုပ်သံဖမ်းခြင်း စတင်မလား။"</string>
<string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ရုပ်သံဖမ်းနေစဉ် Android သည် သင့်ဖန်သားပြင်တွင် မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"အက်ပ်တစ်ခုကို ရုပ်သံဖမ်းနေစဉ် Android သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ရုပ်သံ စဖမ်းရန်"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ၏ အကြောင်းအရာ ရိုက်ကူးခြင်းကို ရပ်ပါမည်"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ၏ အကြောင်းအရာ မျှဝေခြင်းကို ရပ်ပါမည်"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ၏ အကြောင်းအရာ ကာစ်လုပ်ခြင်းကို ရပ်ပါမည်"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‘အမြန် မျှဝေပါ’ နှင့် Find My Device ကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"အသံမျှဝေရန်"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"အသံမျှဝေနေသည်"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ဝိဂျက် ရွေးရန်"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ဝိဂျက် ဖယ်ရှားရန်"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ရွေးချယ်ထားသော ဝိဂျက်ကို တင်ရန်"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"အသုံးပြုသူကို ပြောင်းလဲရန်"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ဆွဲချမီနူး"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ယခု စတင်ပါ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"အကြောင်းကြားချက် မရှိပါ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"အကြောင်းကြားချက်သစ် မရှိပါ"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"အကြောင်းကြားချက်ဟောင်းကြည့်ရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ဤစက်ပစ္စည်းကို သင့်မိဘက စီမံခန့်ခွဲသည်"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ဤစက်ကို သင့်အဖွဲ့အစည်းကပိုင်ဆိုင်ပြီး ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်နိုင်ပါသည်"</string>
@@ -780,7 +793,7 @@
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"သို့မဟုတ်"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ရှာဖွေစာလုံး ရှင်းထုတ်ရန်"</string>
<string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"လက်ကွက်ဖြတ်လမ်းများ"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ဖြတ်လမ်းလင့်ခ်များ ရှာပါ"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ဖြတ်လမ်းများ ရှာရန်"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ဖြတ်လမ်းလင့်ခ် မတွေ့ပါ"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"စနစ်"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"စာရိုက်ခြင်း"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"နောက်ထပ်အချက်အလက်များအတွက် တို့ပါ"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"နှိုးစက်ပေးမထားပါ"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ဖန်သားပြင်လော့ခ် ထည့်ရန်"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"လက်ဗွေ အာရုံခံကိရိယာကို တို့ပါ။ ၎င်းသည် ဖုန်းဘေးရှိ ခလုတ်အတို ဖြစ်သည်"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"လက်ဗွေ အာရုံခံကိရိယာ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"အထောက်အထားစိစစ်ရန်"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"စက်ပစ္စည်းသို့ ဝင်ရန်"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 91ef466..28e7c54 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -74,25 +74,25 @@
<string name="global_action_screenshot" msgid="2760267567509131654">"Skjermdump"</string>
<string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Hold ulåst er slått av"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har sendt et bilde"</string>
- <string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermdumpen …"</string>
- <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Lagrer skjermdumpen i jobbprofilen …"</string>
- <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Lagrer skjermdump i den private profilen"</string>
+ <string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermbildet …"</string>
+ <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Lagrer skjermbildet i jobbprofilen …"</string>
+ <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Lagrer skjermbilde i den private profilen"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Skjermdumpen er lagret"</string>
- <string name="screenshot_failed_title" msgid="3259148215671936891">"Kunne ikke lagre skjermdump"</string>
+ <string name="screenshot_failed_title" msgid="3259148215671936891">"Kunne ikke lagre skjermbilde"</string>
<string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ekstern skjerm"</string>
- <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Enheten må være låst opp før skjermdumpen kan lagres"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv å ta skjermdump på nytt"</string>
- <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Kan ikke lagre skjermdumpen"</string>
- <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper"</string>
- <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"Funksjonen for å ta skjermdumper er blokkert av IT-administratoren din"</string>
+ <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Enheten må være låst opp før skjermbildet kan lagres"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv å ta skjermbilde på nytt"</string>
+ <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Kan ikke lagre skjermbildet"</string>
+ <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisasjonen din tillater ikke at du tar skjermbilder"</string>
+ <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"Funksjonen for å ta skjermbilder er blokkert av IT-administratoren din"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Rediger"</string>
- <string name="screenshot_edit_description" msgid="3333092254706788906">"Rediger skjermdumpen"</string>
+ <string name="screenshot_edit_description" msgid="3333092254706788906">"Rediger skjermbildet"</string>
<string name="screenshot_share_label" msgid="1653061117238861559">"Del"</string>
- <string name="screenshot_share_description" msgid="2861628935812656612">"Del skjermdumpen"</string>
- <string name="screenshot_scroll_label" msgid="2930198809899329367">"Utvidet skjermdump"</string>
- <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Avvis skjermdumpen"</string>
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Del skjermbildet"</string>
+ <string name="screenshot_scroll_label" msgid="2930198809899329367">"Utvidet skjermbilde"</string>
+ <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Avvis skjermbildet"</string>
<string name="screenshot_dismiss_work_profile" msgid="3101530842987697045">"Avvis melding fra jobbprofilen"</string>
- <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermdump"</string>
+ <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermbilde"</string>
<string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"Øvre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Nedre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
<string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Venstre grense <xliff:g id="PERCENT">%1$d</xliff:g> prosent"</string>
@@ -100,8 +100,8 @@
<string name="screenshot_work_profile_notification" msgid="203041724052970693">"Lagret i <xliff:g id="APP">%1$s</xliff:g> i jobbprofilen"</string>
<string name="screenshot_private_profile_notification" msgid="1704440899154243171">"Lagret i <xliff:g id="APP">%1$s</xliff:g> i den private profilen"</string>
<string name="screenshot_default_files_app_name" msgid="8721579578575161912">"Filer"</string>
- <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> har registrert denne skjermdumpen."</string>
- <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åpne apper har registrert denne skjermdumpen."</string>
+ <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> har registrert dette skjermbildet."</string>
+ <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åpne apper har registrert dette skjermbildet."</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Legg til i notat"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"Skjermopptak"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Du slutter å ta opp <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Du slutter å dele <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Du slutter å caste <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funksjoner som Quick Share og Finn enheten min bruker Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth slås på i morgen tidlig"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"velg modul"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"fjern modul"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plasser den valgte modulen"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullegardinmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apper og data i denne økten blir slettet."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Start nå"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ingen varsler"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ingen nye varsler"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås opp for å se eldre varsler"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Denne enheten administreres av forelderen din"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisasjonen din eier denne enheten og kan overvåke nettverkstrafikken"</string>
@@ -792,7 +805,7 @@
<string name="keyboard_shortcut_a11y_filter_open_apps" msgid="6175417687221004059">"Viser snarveier som åpner apper"</string>
<string name="keyboard_shortcut_a11y_filter_current_app" msgid="7944592357493737911">"Viser snarveier for den gjeldende appen"</string>
<string name="group_system_access_notification_shade" msgid="1619028907006553677">"Se varsler"</string>
- <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ta skjermdump"</string>
+ <string name="group_system_full_screenshot" msgid="5742204844232667785">"Ta skjermbilde"</string>
<string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Vis snarveier"</string>
<string name="group_system_go_back" msgid="2730322046244918816">"Gå tilbake"</string>
<string name="group_system_access_home_screen" msgid="4130366993484706483">"Gå til startskjermen"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Trykk for å få mer informasjon"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ingen alarm angitt"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"legg inn skjermlåsen"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Trykk på fingeravtrykkssensoren. Det er den korte knappen på siden av telefonen."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeravtrykkssensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentiser"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"åpne enheten"</string>
@@ -1289,8 +1303,8 @@
<string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"Vil du bytte skjerm?"</string>
<string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"Bruk baksidekameraet for å få høyere oppløsning"</string>
<string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"Brett ut telefonen for å få høyere oppløsning"</string>
- <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En sammenleggbar enhet blir brettet ut"</string>
- <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En sammenleggbar enhet blir snudd"</string>
+ <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"En foldbar enhet blir brettet ut"</string>
+ <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"En foldbar enhet blir snudd"</string>
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"lagt sammen"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"åpen"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 10d64b7..5d42881 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> का सामग्री रेकर्ड हुन छाड्ने छन्"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> का सामग्री सेयर हुन छाड्ने छन्"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> का सामग्री कास्ट हुन छाड्ने छन्"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक सेयर र Find My Device जस्ता सुविधाहरू प्रयोग गर्न ब्लुटुथ चाहिन्छ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"अडियो सेयर गर्नुहोस्"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"अडियो सेयर गरिँदै छ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"विजेट चयन गर्नुहोस्"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"विजेट हटाउनुहोस्"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"चयन गरिएका विजेटका लागि ठाउँ चयन गर्नुहोस्"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"प्रयोगकर्ता फेर्नुहोस्"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेनु"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"अहिले न"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"कुनै सूचनाहरू छैनन्"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"कुनै पनि नयाँ सूचना छैन"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"पुराना सूचनाहरू हेर्न अनलक गर्नुहोस्"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"यो डिभाइस तपाईंका अभिभावक व्यवस्थापन गर्नुहुन्छ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"यो डिभाइस तपाईंको सङ्गठनको स्वामित्वमा छ र उक्त सङ्गठनले यसको नेटवर्क ट्राफिक अनुगमन गर्न सक्छ"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"थप जानकारी प्राप्त गर्न ट्याप गर्नुहोस्"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"अलार्म राखिएको छैन"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"स्क्रिन लक हाल्नुहोस्"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"फिंगरप्रिन्ट सेन्सरमा छुनुहोस्। यो फोनको साइडमा भएको अलि सानो चाहिँ बटन हो"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फिंगरप्रिन्ट सेन्सर"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"प्रमाणित गर्नुहोस्"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"डिभाइस हाल्नुहोस्"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c990cab..d53f556 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Je stopt met het opnemen van <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Je stopt met het delen van <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Je stopt met het casten van <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Functies zoals Quick Share en Vind mijn apparaat gebruiken bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audio delen"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio delen"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"widget selecteren"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"widget verwijderen"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"geselecteerde widget plaatsen"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Nu starten"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Geen meldingen"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nieuwe meldingen"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Aanpasbare meldingen staan aan"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Je apparaat verlaagt nu het volume en vermindert pop-ups op het scherm gedurende maximaal 2 minuten als je in korte tijd veel meldingen krijgt."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Uitzetten"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontgrendel om oudere meldingen te zien"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dit apparaat wordt beheerd door je ouder"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Je organisatie is eigenaar van dit apparaat en kan het netwerkverkeer bijhouden"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tik hier voor meer informatie"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Geen wekker gezet"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"schermvergrendeling invoeren"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Raak de vingerafdruksensor aan. Dit is de kortere knop aan de zijkant van de telefoon."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Vingerafdruksensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"verifiëren"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"apparaat opgeven"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Terug"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Als je wilt teruggaan, swipe je met 3 vingers naar links of rechts op de touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad met 3 vingers die naar rechts en links bewegen"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Apparaatscherm met animatie voor teruggebaar"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 49a3268..8608471 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"ଆପଣ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ରେକର୍ଡ କରିବା ବନ୍ଦ କରିବେ"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"ଆପଣ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ସେୟାର କରିବା ବନ୍ଦ କରିବେ"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"ଆପଣ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ କାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ଏବଂ Find My Device ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ଅଡିଓ ସେୟାର କରନ୍ତୁ"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ୱିଜେଟ ଚୟନ କରନ୍ତୁ"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ୱିଜେଟକୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ଚୟନିତ ୱିଜେଟ ରଖନ୍ତୁ"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍ ବଦଳାନ୍ତୁ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ପୁଲଡାଉନ ମେନୁ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"କୌଣସି ନୂଆ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ନାହିଁ"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ଆଡେପ୍ଟିଭ ବିଜ୍ଞପ୍ତି ଚାଲୁ ଅଛି"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"କମ ସମୟ ମଧ୍ୟରେ ଅନେକ ବିଜ୍ଞପ୍ତି ପାଇଲେ ଆପଣଙ୍କ ଡିଭାଇସ ଏବେ ଦୁଇ ମିନିଟ ପର୍ଯ୍ୟନ୍ତ ଭଲ୍ୟୁମକୁ କମ କରି ସ୍କ୍ରିନରେ ଥିବା ପପ-ଅପକୁ କମ କରେ।"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ପୁରୁଣା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଦେଖିବାକୁ ଅନଲକ କରନ୍ତୁ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ଏହି ଡିଭାଇସ୍ ଆପଣଙ୍କ ବାପାମାଙ୍କ ଦ୍ୱାରା ପରିଚାଳିତ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ଏହି ଡିଭାଇସର ମାଲିକାନା ଆପଣଙ୍କ ସଂସ୍ଥା ପାଖରେ ଅଛି ଏବଂ ଏହା ନେଟୱାର୍କ ଟ୍ରାଫିକର ନିରୀକ୍ଷଣ କରିପାରେ"</string>
@@ -811,7 +821,7 @@
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ଡାହାଣପଟର ବା ତଳର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ବାମପଟର ବା ଉପରର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ସମୟରେ: କୌଣସି ଆପକୁ ଗୋଟିଏରୁ ଅନ୍ୟ ଏକ ଆପରେ ବଦଳାନ୍ତୁ"</string>
- <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ଇନପୁଟ କରନ୍ତୁ"</string>
+ <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ଇନପୁଟ"</string>
<string name="input_switch_input_language_next" msgid="3782155659868227855">"ପରବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"ପୂର୍ବବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"ଇମୋଜି ଆକ୍ସେସ କରନ୍ତୁ"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ଅଧିକ ସୂଚନା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ଆଲାରାମ ସେଟ ହୋଇନାହିଁ"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ସ୍କ୍ରିନ ଲକ ଲେଖନ୍ତୁ"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ ଏହା ଫୋନର ପାର୍ଶ୍ୱରେ ଥିବା ଛୋଟ ବଟନ ଅଟେ"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ଟିପଚିହ୍ନ ସେନ୍ସର୍"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ପ୍ରମାଣୀକରଣ କରନ୍ତୁ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ଡିଭାଇସ୍ ବିଷୟରେ ସୂଚନା ଲେଖନ୍ତୁ"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ପଛକୁ ଫେରିବା ପାଇଁ ଯେ କୌଣସି ସ୍ଥାନରେ ତିନି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ବାମ କିମ୍ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ।"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ଡାହାଣ ଏବଂ ବାମକୁ ତିନି ଆଙ୍ଗୁଠି ମୁଭ କରୁଥିବା ଟଚପେଡ ଦେଖାଉଛି"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ଡିଭାଇସ ସ୍କ୍ରିନ ବେକ ଜେଶ୍ଚର ପାଇଁ ଆନିମେସନ ଦେଖାଉଛି"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 6a7e9e8..9fabdae 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"ਤੁਸੀਂ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਰਿਕਾਰਡ ਕਰਨਾ ਬੰਦ ਕਰ ਦੇਵੋਗੇ"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"ਤੁਸੀਂ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਸਾਂਝਾ ਕਰਨਾ ਬੰਦ ਕਰ ਦੇਵੋਗੇ"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"ਤੁਸੀਂ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰ ਦੇਵੋਗੇ"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ਕਵਿੱਕ ਸ਼ੇਅਰ ਅਤੇ Find My Device ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਵਰਤਦੀਆਂ ਹਨ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕਰੋ"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ਵਿਜੇਟ ਚੁਣੋ"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ਵਿਜੇਟ ਹਟਾਓ"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ਚੁਣੇ ਗਏ ਵਿਜੇਟ ਲਈ ਥਾਂ ਚੁਣੋ"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿਚਲੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟੇ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ਹੁਣੇ ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ਕੋਈ ਨਵੀਂ ਸੂਚਨਾ ਨਹੀਂ ਹੈ"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"ਅਡੈਪਟਿਵ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਹਨ"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ਘੱਟ ਸਮੇਂ \'ਚ ਕਈ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ, ਡੀਵਾਈਸ ਹੁਣ ਦੋ ਮਿੰਟਾਂ ਲਈ ਸਕ੍ਰੀਨ \'ਤੇ ਅਵਾਜ਼ ਅਤੇ ਪੌਪ-ਅੱਪ ਘਟਾ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ਬੰਦ ਕਰੋ"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ਪੁਰਾਣੀਆਂ ਸੂਚਨਾਵਾਂ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਕੋਲ ਇਸ ਡੀਵਾਈਸ ਦੀ ਮਲਕੀਅਤ ਹੈ ਅਤੇ ਇਹ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ਕੋਈ ਅਲਾਰਮ ਸੈੱਟ ਨਹੀਂ"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ਸਕ੍ਰੀਨ ਲਾਕ ਦਾਖਲ ਕਰੋ"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪਰਸ਼ ਕਰੋ। ਇਹ ਫ਼ੋਨ ਦੇ ਇੱਕ ਪਾਸੇ ਦਿੱਤਾ ਛੋਟਾ ਜਿਹਾ ਬਟਨ ਹੈ"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ਪ੍ਰਮਾਣਿਤ ਕਰੋ"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ਡੀਵਾਈਸ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ਵਾਪਸ ਜਾਓ"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ਵਾਪਸ ਜਾਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਕਿਤੇ ਵੀ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨੂੰ ਸੱਜੇ ਅਤੇ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਲਿਜਾਂਦੇ ਹੋਏ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ਡੀਵਾਈਸ ਦੀ ਸਕ੍ਰੀਨ \'ਤੇ ਪਿੱਛੇ ਜਾਣ ਵਾਲੇ ਇਸ਼ਾਰੇ ਲਈ ਐਨੀਮੇਸ਼ਨ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index c154fc9b..f592e09 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Przestaniesz nagrywać treści z aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Przestaniesz udostępniać treści z aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Przestaniesz przesyłać treści z aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetootha używają funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Udostępnij dźwięk"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Udostępnia dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
@@ -390,7 +391,7 @@
<string name="thermal" msgid="6758074791325414831">"Termografia"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string>
- <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywny"</string>
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywne"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Rozłączono"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Urządzenia słuchowe"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"wybierz widżet"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"usuń widżet"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"umieść wybrany widżet"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Rozpocznij teraz"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Brak powiadomień"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Brak nowych powiadomień"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odblokuj i zobacz starsze powiadomienia"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Tym urządzeniem zarządza Twój rodzic"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Twoja organizacja jest właścicielem tego urządzenia i może monitorować ruch w sieci"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Kliknij, aby uzyskać więcej informacji"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nie ustawiono alarmu"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"Wprowadź blokadę ekranu"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dotknij czytnika linii papilarnych. To ten krótszy przycisk z boku telefonu"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Czytnik linii papilarnych"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"uwierzytelnij"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"otwórz urządzenie"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2e64391..0c27fea 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -107,7 +107,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Iniciar gravação?"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Enquanto você grava, o Android tem acesso a todas as informações na tela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Durante a gravação, o Android tem acesso às informações na tela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Enquanto você grava um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Iniciar gravação"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Gravar áudio"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Você vai parar de gravar o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Você vai parar de compartilhar o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Você vai parar de transmitir o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartilhar áudio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecionar widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remover widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nenhuma notificação nova"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificações adaptáveis ativadas"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O dispositivo agora diminui o volume e reduz os pop-ups na tela por até 2 minutos quando você recebe muitas notificações em pouco tempo."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie p/ acessar notificações antigas"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo é gerenciado pelo seu familiar responsável"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Sua organização é dona deste dispositivo e pode monitorar o tráfego de rede"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para mais informações"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"inserir bloqueio de tela"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toque no sensor de impressão digital. É o menor botão na lateral do smartphone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressão digital"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mostrando 3 dedos deslizando para a direita e esquerda"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Tela do dispositivo mostrando a animação do gesto de volta"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index ca0f171..93359e4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Vai parar de gravar <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Vai parar de partilhar <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Vai parar de transmitir <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partilhar áudio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"A partilhar áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -382,14 +383,14 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório de erro"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da experiência do disposit. foi afetada?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte do dispositivo foi afetada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecione o tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de ecrã"</string>
<string name="performance" msgid="6552785217174378320">"Desempenho"</string>
<string name="user_interface" msgid="3712869377953950887">"Interface do utilizador"</string>
<string name="thermal" msgid="6758074791325414831">"Térmico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
- <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
+ <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Disp. auditivos"</string>
<string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
<string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desligados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
@@ -469,7 +470,7 @@
<string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ignorar"</string>
<string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Adicionar, remover e reordenar widgets neste espaço"</string>
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Adicionar mais widgets"</string>
- <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha premido para personalizar os widgets"</string>
+ <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantenha pressionado para personalizar os widgets"</string>
<string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
<string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ícone da app do widget desativado"</string>
<string name="icon_description_for_pending_widget" msgid="8413816401868001755">"Ícone da app para um widget que está a ser instalado"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecionar widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remover widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu pendente"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Começar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Não existem novas notificações"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificações adaptáveis ativas"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O disp. diminui o vol. e reduz os pop-ups por até 2 min quando recebe muitas notificações seguidas."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie e veja notificações antigas"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo é gerido pelos teus pais"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"A sua entidade gere este dispositivo e pode monitorizar o tráfego de rede."</string>
@@ -621,7 +631,7 @@
<string name="stream_voice_call" msgid="7468348170702375660">"Chamada"</string>
<string name="stream_system" msgid="7663148785370565134">"Sistema"</string>
<string name="stream_ring" msgid="7550670036738697526">"Toque"</string>
- <string name="stream_music" msgid="2188224742361847580">"Suporte de dados"</string>
+ <string name="stream_music" msgid="2188224742361847580">"Multimédia"</string>
<string name="stream_alarm" msgid="16058075093011694">"Alarme"</string>
<string name="stream_notification" msgid="7930294049046243939">"Notificação"</string>
<string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para obter mais informações"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"introduzir bloqueio de ecrã"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toque no sensor de impressões digitais. É o botão mais pequeno na parte lateral do telemóvel"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressões digitais"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"entrar no dispositivo"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para retroceder, deslize rapidamente para a esquerda ou direita com três dedos em qualquer parte do touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad a mostrar três dedos a moverem-se para a direita e esquerda"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ecrã de dispositivo a mostrar uma animação do gesto para retroceder"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2e64391..0c27fea 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -107,7 +107,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Iniciar gravação?"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Enquanto você grava, o Android tem acesso a todas as informações na tela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Durante a gravação, o Android tem acesso às informações na tela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Enquanto você grava um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Iniciar gravação"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Gravar áudio"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Você vai parar de gravar o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Você vai parar de compartilhar o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Você vai parar de transmitir o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartilhar áudio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartilhando áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selecionar widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"remover widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"posicionar widget selecionado"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nenhuma notificação nova"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificações adaptáveis ativadas"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"O dispositivo agora diminui o volume e reduz os pop-ups na tela por até 2 minutos quando você recebe muitas notificações em pouco tempo."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie p/ acessar notificações antigas"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Este dispositivo é gerenciado pelo seu familiar responsável"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Sua organização é dona deste dispositivo e pode monitorar o tráfego de rede"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para mais informações"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"inserir bloqueio de tela"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Toque no sensor de impressão digital. É o menor botão na lateral do smartphone"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressão digital"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mostrando 3 dedos deslizando para a direita e esquerda"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Tela do dispositivo mostrando a animação do gesto de volta"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 36457fd..eab31ce 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Nu vei mai înregistra <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Nu vei mai permite accesul la <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Nu vei mai proiecta <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcții precum Quick Share și Găsește-mi dispozitivul folosesc Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se va activa mâine dimineață"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Trimite audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Se permite accesul la conținutul audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"selectează un widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"elimină widgetul"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plasează widgetul selectat"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nicio notificare"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nicio notificare nouă"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Notificări adaptabile activate"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Dispozitivul reduce acum volumul și numărul de ferestre pop-up de pe ecran timp de până la două minute când primești multe notificări într-un timp scurt"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Dezactivează"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Deblochează ca să vezi notificări vechi"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dispozitivul este gestionat de unul dintre părinți"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizația ta deține acest dispozitiv și poate monitoriza traficul de rețea"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Atinge pentru mai multe informații"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nicio alarmă setată"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"intră în blocarea ecranului"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Atinge senzorul de amprentă. Acesta este butonul mai scurt de pe partea laterală a telefonului"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor de amprentă"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifică-te"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesează dispozitivul"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Înapoi"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Pentru a reveni, glisează spre stânga sau spre dreapta cu trei degete oriunde pe touchpad."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad cu trei degete care se mișcă spre dreapta și spre stânga"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ecran de dispozitiv cu o animație pentru gestul Înapoi"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index fe0e8db..be63043 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Запись в приложении <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> будет прекращена."</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Демонстрация экрана приложения <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> будет прекращена."</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Трансляция из приложения <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> будет прекращена."</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth используется в таких функциях и сервисах, как \"Быстрая отправка\" и \"Найти устройство\""</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Отправить аудио"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Отправка аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -397,7 +398,7 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string>
- <string name="live_caption_title" msgid="8916875614623730005">"Автоматические субтитры"</string>
+ <string name="live_caption_title" msgid="8916875614623730005">"Автосубтитры"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"выбрать виджет"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"удалить виджет"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"разместить выбранный виджет"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Сменить пользователя."</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"раскрывающееся меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Начать"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Нет уведомлений."</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Новых уведомлений нет"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблокируйте, чтобы увидеть уведомления"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Устройством управляет один из родителей."</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ваша организация управляет этим устройством и может отслеживать сетевой трафик"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Нажмите, чтобы узнать больше."</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Нет будильников"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"разблокировать экран"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Коснитесь сканера отпечатков пальцев. Это самая короткая кнопка сбоку на телефоне."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер отпечатков пальцев"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"выполнить аутентификацию"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"указать устройство"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 8357008..7462df6 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"ඔබ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> පටිගත කිරීම නතර කරනු ඇත"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"ඔබ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> බෙදා ගැනීම නතර කරනු ඇත"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"ඔබ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> විකාශය නතර කරනු ඇත"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ඉක්මන් බෙදා ගැනීම සහ මගේ උපාංගය සෙවීම වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්රීය වෙයි"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ශ්රව්ය බෙදා ගන්න"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ශ්රව්ය බෙදා ගැනීම"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"විජට්ටුව තෝරන්න"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"විජට්ටුව ඉවත් කරන්න"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"තෝරන ලද විජට්ටුව තබන්න"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"පරිශීලක මාරුව"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"නිපතන මෙනුව"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"දැන් අරඹන්න"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"දැනුම්දීම් නැත"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"නව දැනුම්දීම් නැත"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"අනුවර්තන දැනුම්දීම් ක්රියාත්මකයි"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"ඔබේ උපාංගය දැන් ශබ්දය අඩු කරන අතර ඔබට කෙටි කාල පරාසයක් තුළ බොහෝ දැනුම්දීම් ලැබෙන විට තිරය මත උත්පතන විනාඩි දෙකක් දක්වා අඩු කරයි."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ක්රියාවිරහිත කරන්න"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"පැරණි දැනුම්දීම් බැලීමට අගුළු හරින්න"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"මෙම උපාංගය ඔබගේ මාපියන්ගෙන් අයකු විසින් කළමනාකරණය කෙරේ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ඔබේ සංවිධානයට මෙම උපාංගය අයිති අතර ජාල තදබදය නිරීක්ෂණය කළ හැකිය"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"තවත් තොරතුරු සඳහා තට්ටු කරන්න"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"එලාම සකසා නැත"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"තිර අගුල ඇතුළු කරන්න"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න. එය දුරකථනයේ පැත්තේ ඇති කෙටි බොත්තමයි"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ඇඟිලි සලකුණු සංවේදකය"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"සත්යාපනය කරන්න"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"උපාංගය ඇතුළු කරන්න"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ආපස්සට යන්න"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"ආපසු යාමට, ස්පර්ශ පුවරුවේ ඕනෑම තැනක ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ඇඟිලි තුනක් දකුණට සහ වමට චලනය වන බව පෙන්වන ස්පර්ශක පුවරුව"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"පසුපස අභිනය සඳහා සජීවිකරණය පෙන්වන උපාංග තිරය"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 148b390..6202a7e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Prestante zaznamenávať obsah aplikácie <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Prestanete zdieľať obsah aplikácie <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Prestanete prenášať obsah aplikácie <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcie ako Quick Share a Nájdi moje zariadenie používajú Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sa zapne zajtra ráno"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Zdieľať zvuk"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zdieľa sa zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
@@ -378,8 +379,8 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začať"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončiť"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Problém s nahrávaním"</string>
- <string name="qs_record_issue_start" msgid="2979831312582567056">"Začnite"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Nahrať problém"</string>
+ <string name="qs_record_issue_start" msgid="2979831312582567056">"Začať"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Zastavte"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hlásenie chyby"</string>
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vybrať miniaplikáciu"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstrániť miniaplikáciu"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"prepnúť vybranú miniaplikáciu"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rozbaľovacia ponuka"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Spustiť"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Žiadne upozornenia"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Žiadne nové upozornenia"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Adaptívne upozornenia sú zap."</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Zariadenie teraz zníži hlasitosť a zredukuje vyskakovacie okná na obrazovke na dve až štyri minúty, keď dostanete veľa upozornení v krátkom čase."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vypnúť"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odomknutím zobrazíte staršie upozornenia"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Toto zariadenie spravuje tvoj rodič"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša organizácia spravuje toto zariadenie a môže sledovať sieťovú premávku"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Klepnutím si zobrazíte ďalšie informácie"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Žiadny budík"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"zadať zámku obrazovky"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dotknite sa senzora odtlačkov prstov. Ide o kratšie tlačidlo na boku telefónu."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor odtlačkov prstov"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"overte"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"vstúpte do zariadenia"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Prejsť späť"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Ak chcete prejsť späť, potiahnite doľava alebo doprava troma prstami kdekoľvek na touchpade."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tri prsty na touchpade pohybujúce sa doprava a doľava"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Obrazovka zariadenia, na ktorej je animácia gesta späť"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 489551d..2f8dc70 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Ustavili boste snemanje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Ustavili boste deljenje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Ustavili boste predvajanje aplikacije <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, uporabljajo Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se bo vklopil jutri zjutraj"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deli zvok"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Poteka deljenje zvoka"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"izberite pripomoček"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"odstranitev pripomočka"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"postavitev izbranega pripomočka"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Preklop med uporabniki"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"spustni meni"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Začni zdaj"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Ni obvestil"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Ni novih obvestil"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odklenite za ogled starejših obvestil"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"To napravo upravlja tvoj starš"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Vaša organizacija je lastnica te naprave in lahko nadzira omrežni promet"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dotaknite se za več informacij"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ni nastavljenih alarmov"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"odklenite zaslon"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Dotaknite se tipala prstnih odtisov. To je krajši gumb ob strani telefona."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Tipalo prstnih odtisov"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"preverjanje pristnosti"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"vstop v napravo"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 5b74b1e..fcc19c1 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Do të ndalosh regjistrimin me <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Do të ndalosh ndarjen e <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Do të ndalosh transmetimin e <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Veçoritë e tilla si \"Ndarja e shpejtë\" dhe \"Gjej pajisjen time\" përdorin Bluetooth-in"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-i do të aktivizohet nesër në mëngjes"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Ndaj audion"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioja po ndahet"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"zgjidh miniaplikacionin"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"hiq miniaplikacionin"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"vendos miniaplikacionin e zgjedhur"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyja me tërheqje poshtë"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Fillo tani"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Asnjë njoftim"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Nuk ka njoftime të reja"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Shkyç për të parë njoftimet e vjetra"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Kjo pajisje menaxhohet nga prindi yt"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizata jote e zotëron këtë pajisje dhe mund të monitorojë trafikun e rrjetit"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Trokit për më shumë informacione"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nuk është caktuar asnjë alarm"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"hyr te kyçja e ekranit"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Prek sensorin e gjurmës së gishtit. Është butoni më i shkurtër në pjesën anësore të telefonit"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensori i gjurmës së gishtit"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"për ta vërtetuar"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"për të hyrë në pajisje"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1877274..ad3eb23 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Зауставићете снимање за: <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Зауставићете дељење за: <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Зауставићете пребацивање за: <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функције као што су Quick Share и Пронађи мој уређај користе Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Дели звук"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Дели се звук"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"изаберите виџет"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"уклоните виџет"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"поставите изабрани виџет"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"падајући мени"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Започни"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Нема обавештења"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Нема нових обавештења"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Прилаг. обавештења су укључена"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Уређај сада смањује звук и број искачућих прозора на екрану до 2 минута кад примите много обавештења у кратком року."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Искључи"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Откључајте за старија обавештења"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Овим уређајем управља родитељ"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Организација је власник уређаја и може да надгледа мрежни саобраћај"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Додирните за више информација"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Није подешен"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"унесите откључавање екрана"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Додирните сензор за отисак прста. То је краће дугме на бочној страни телефона"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отисак прста"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"потврдите идентитет"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"унесите уређај"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Да бисте се вратили, превуците улево или удесно са три прста било где на тачпеду."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Тачпед са приказом три прста који се померају удесно и улево"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран уређаја са приказом анимације покрета за назад"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index dc4dea1..5bc3a26 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Du slutar att spela in <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Du slutar att dela <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Du slutar att casta <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som Snabbdelning och Hitta min enhet använder Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveras i morgon bitti"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dela ljud"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Delar ljud"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"välj widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ta bort widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"placera vald widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"rullgardinsmeny"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Starta nu"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Inga aviseringar"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Det finns inga nya aviseringar"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Anpassade aviseringar är på"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Om du får många aviseringar tätt inpå varandra sänker nu enheten volymen och minskar antalet popuper i upp till två minuter."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Inaktivera"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Lås upp för att se äldre aviseringar"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Den här enheten hanteras av din förälder"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organisationen äger den här enheten och kan övervaka nätverkstrafiken"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tryck för mer information"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Inget inställt alarm"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ange skärmlåset"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Tryck på fingeravtryckssensorn. Det är den mindre knappen på sidan av telefonen"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeravtryckssensor"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentisera"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ange enhet"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tillbaka"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Gå tillbaka genom att svepa åt vänster eller höger med tre fingrar var som helst på styrplattan."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tre fingrar rör sig åt höger och vänster på en styrplatta"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"En enhetsskärm visar en animation för rörelsen Tillbaka"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 7bfef43..368d5c5 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -107,7 +107,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
<string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Ungependa kuanza kurekodi?"</string>
- <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Unaporekodi, Android inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
+ <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Unaporekodi, Android inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Unaporekodi programu, Android inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
<string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Anza kurekodi"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Rekodi sauti"</string>
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Utaacha kurekodi maudhui ya <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Utakomesha ufikiaji wa maudhui ya <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Utaacha kutuma maudhui ya <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Vipengele kama vile Kutuma Haraka na Tafuta Kifaa Changu hutumia Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sikiliza pamoja na wengine"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Mnasikiliza pamoja"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -378,7 +379,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekodi ya skrini"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Anza kurekodi"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Acha kurekodi"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"Hitilafu ya Kurekodi"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodi Hitilafu"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"Anza"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"Simamisha"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Ripoti ya Hitilafu"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"chagua wijeti"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ondoa wijeti"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"weka wijeti uliyochagua"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Badili mtumiaji"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menyu ya kuvuta chini"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Anza sasa"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Hakuna arifa"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Hakuna arifa mpya"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Umewasha arifa zinazojirekebisha"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Sasa kifaa chako kinapunguza sauti na idadi ya madirisha ibukizi kwenye skrini kwa hadi dakika mbili ukipokea arifa nyingi kwa muda mfupi."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Zima"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Fungua ili uone arifa za zamani"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Kifaa hiki kinadhibitiwa na mzazi wako"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Shirika lako linamiliki kifaa hiki na huenda likafuatilia trafiki ya mtandao"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Gusa ili upate maelezo zaidi"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Hujaweka kengele"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"weka kifunga skrini"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Gusa kitambuzi cha alama ya kidole. Ni kitufe kifupi zaidi kilicho pembeni mwa simu"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Kitambua alama ya kidole"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"thibitisha"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"weka kifaa"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Rudi nyuma"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Ili kurudi nyuma, telezesha vidole vitatu kushoto au kulia mahali popote kwenye padi ya kugusa."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Padi ya kugusa inayoonyesha vidole vitatu vikisonga kulia na kushoto"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skrini ya kifaa inayoonyesha uhuishaji wa mguso wa nyuma"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 6298363..702ad22 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை ரெக்கார்டு செய்வதை நிறுத்துவீர்கள்"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸைப் பகிர்வதை நிறுத்துவீர்கள்"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அலைபரப்புவதை நிறுத்துவீர்கள்"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்கள் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ஆடியோவைப் பகிர்"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ஆடியோ பகிரப்படுகிறது"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"விட்ஜெட்டைத் தேர்ந்தெடுக்கும்"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"விட்ஜெட்டை அகற்றும்"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"தேர்ந்தெடுத்த விட்ஜெட்டைக் காட்சிப்படுத்தும்"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"பயனரை மாற்று"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"கீழ் இழுக்கும் மெனு"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"இப்போது தொடங்கு"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"அறிவிப்புகள் இல்லை"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"புதிய அறிவிப்புகள் இல்லை"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"பழைய அறிவிப்பைப் பார்க்க அன்லாக் செய்க"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"இந்தச் சாதனம் உங்கள் பெற்றோரால் நிர்வகிக்கப்படுகிறது"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு உரியது, நெட்வொர்க் ட்ராஃபிக்கையும் நிறுவனமே கண்காணிக்கக்கூடும்"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"மேலும் தகவல்களுக்கு தட்டவும்"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"அலாரம் எதுவுமில்லை"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"திரைப் பூட்டை உள்ளிடலாம்"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"கைரேகை சென்சாரைத் தொடவும். இது மொபைலின் பக்கவாட்டில் உள்ள சிறிய பட்டன் ஆகும்"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"கைரேகை சென்சார்"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"அங்கீகரி"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"சாதனத்தைத் திற"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index e3238fe..3dd7293 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"మీరు <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను రికార్డ్ చేయడం ఆపివేస్తారు"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"మీరు <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను షేర్ చేయడం ఆపివేస్తారు"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"మీరు <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను ప్రసారం చేయడం ఆపివేస్తారు"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"క్విక్ షేర్, Find My Device వంటి ఫీచర్లు బ్లూటూత్ను ఉపయోగిస్తాయి"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ఆడియోను షేర్ చేయండి"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ఆడియోను షేర్ చేస్తున్నారు"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"విడ్జెట్ను ఎంచుకోండి"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"విడ్జెట్ను తీసివేయండి"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ఎంచుకున్న విడ్జెట్ కోసం ప్లేస్ను ఎంచుకోండి"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"వినియోగదారుని మార్చు"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"పుల్డౌన్ మెనూ"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"నోటిఫికేషన్లు లేవు"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"కొత్త నోటిఫికేషన్లు ఏవీ లేవు"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"అనుకూల నోటిఫికేషన్లు ఆన్లో ఉన్నాయి"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"మీరు తక్కువ సమయంలో అనేక నోటిఫికేషన్లను స్వీకరించినప్పుడు మీ పరికరం ఇప్పుడు వాల్యూమ్ను తగ్గిస్తుంది, స్క్రీన్పై పాప్-అప్లను రెండు నిమిషాల వరకు తగ్గిస్తుంది."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ఆఫ్ చేయండి"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"పాత నోటిఫికేషన్ల కోసం అన్లాక్ చేయండి"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"ఈ పరికరం మీ సంస్థకు చెందినది, కాబట్టి అది నెట్వర్క్ ట్రాఫిక్ను పర్యవేక్షించవచ్చు"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"మరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"అలారం సెట్ చేయలేదు"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"స్క్రీన్ లాక్ను ఎంటర్ చేయండి"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"వేలిముద్ర సెన్సార్ను తాకండి. ఇది ఫోన్కు పక్కన ఉండే బటన్లలో చిన్నది"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"వేలిముద్ర సెన్సార్"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ప్రామాణీకరించండి"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"పరికరాన్ని ఎంటర్ చేయండి"</string>
@@ -1296,7 +1307,7 @@
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
<string name="stylus_battery_low_percentage" msgid="2564243323894629626">"స్టయిలస్ బ్యాటరీ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"మీ స్టైలస్ను ఛార్జర్కి కనెక్ట్ చేయండి"</string>
- <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string>
+ <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టయిలస్ బ్యాటరీ"</string>
<string name="video_camera" msgid="7654002575156149298">"వీడియో కెమెరా"</string>
<string name="call_from_work_profile_title" msgid="5418253516453177114">"వ్యక్తిగత యాప్ నుండి కాల్ చేయడం సాధ్యం కాదు"</string>
<string name="call_from_work_profile_text" msgid="2856337395968118274">"మీ సంస్థ, వర్క్ యాప్ల నుండి మాత్రమే కాల్స్ చేయడానికి మిమ్మల్ని అనుమతిస్తుంది"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"వెనుకకు"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"వెనుకకు వెళ్లడానికి, టచ్ప్యాడ్లో ఎక్కడైనా మూడు వేళ్లను ఉపయోగించి ఎడమ లేదా కుడికి స్వైప్ చేయండి."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"మూడు వేళ్లు కుడి, ఎడమకు కదులుతున్నట్లు చూపే టచ్ప్యాడ్"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"వెనుక సంజ్ఞ కోసం యానిమేషన్ను చూపుతున్న పరికర స్క్రీన్"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్లైట్"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d21bf5e..92f4bc9 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"คุณจะหยุดการบันทึก <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"คุณจะหยุดการแชร์ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"คุณจะหยุดการแคสต์ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และ \"หาอุปกรณ์ของฉัน\" ใช้บลูทูธ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"แชร์เสียง"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"กำลังแชร์เสียง"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string>
@@ -378,7 +379,7 @@
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"บันทึกหน้าจอ"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"เริ่ม"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"หยุด"</string>
- <string name="qs_record_issue_label" msgid="8166290137285529059">"ปัญหาการบันทึก"</string>
+ <string name="qs_record_issue_label" msgid="8166290137285529059">"บันทึกปัญหา"</string>
<string name="qs_record_issue_start" msgid="2979831312582567056">"เริ่ม"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"หยุด"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"รายงานข้อบกพร่อง"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"เลือกวิดเจ็ต"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"นำวิดเจ็ตออก"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"จัดวางวิดเจ็ตที่เลือก"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"สลับผู้ใช้"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"เมนูแบบเลื่อนลง"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"เริ่มเลย"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"ไม่มีการแจ้งเตือน"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"ไม่มีการแจ้งเตือนใหม่"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ปลดล็อกเพื่อดูการแจ้งเตือนเก่า"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"อุปกรณ์นี้จัดการโดยผู้ปกครอง"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"แตะดูข้อมูลเพิ่มเติม"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ไม่มีการตั้งปลุก"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ป้อนข้อมูลการล็อกหน้าจอ"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"แตะเซ็นเซอร์ลายนิ้วมือ ซึ่งเป็นปุ่มสั้นๆ ที่อยู่ด้านข้างโทรศัพท์"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"เซ็นเซอร์ลายนิ้วมือ"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"ตรวจสอบสิทธิ์"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"เข้าถึงอุปกรณ์"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index b63d282..d8c819f 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Hihinto ka sa pag-record ng <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Hihinto ka sa pagbabahagi ng <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Hihinto ka sa pag-cast ng <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Gumagamit ng Bluetooth ang mga feature tulad ng Quick Share at Hanapin ang Aking Device"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Mag-o-on ang Bluetooth bukas ng umaga"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Ibahagi ang audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ibinabahagi ang audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"pumili ng widget"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"alisin ang widget"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"ilagay ang napiling widget"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Magpalit ng user"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pulldown menu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Magsimula ngayon"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Walang mga notification"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Walang bagong notification"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"I-unlock para makita ang mga mas lumang notification"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Pinapamahalaan ng magulang mo itong device"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Pagmamay-ari ng organisasyon mo ang device na ito at puwede nitong subaybayan ang trapiko sa network"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"I-tap para sa higit pang impormasyon"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Walang alarm"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ilagay ang lock ng screen"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Pindutin ang sensor para sa fingerprint. Ito ang mas maikling button sa gilid ng telepono"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor para sa fingerprint"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"i-authenticate"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"ilagay ang device"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f78afe4..7eb08dd 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> içeriğini kaydetmeyi durdurursunuz"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> içeriğini paylaşmayı durdurursunuz"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> içeriğini yayınlamayı durdurursunuz"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ve Cihazımı Bul gibi özellikler Bluetooth\'u kullanır"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth yarın sabah açılacak"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Sesi paylaş"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ses paylaşılıyor"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"widget seçin"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"widget\'ı kaldır"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"seçilen widget\'ı yerleştir"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Kullanıcı değiştirme"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"açılır menü"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Şimdi başlat"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Bildirim yok"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Yeni bildirim yok"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Uyarlanabilir bildirimler açık"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Cihazınız, kısa süre içinde çok sayıda bildirim aldığınızda artık iki dakika boyunca sesi kısar ve ekrandaki pop-up\'ları azaltır."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Kapat"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eski bildirimler için kilidi açın"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Bu cihaz ebeveyniniz tarafından yönetiliyor"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Bu cihaz, kuruluşunuza ait olup ağ trafiği kuruluşunuz tarafından izlenebilir"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Daha fazla bilgi için dokunun"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarm yok"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ekran kilidini gir"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Parmak izi sensörüne dokunun. Sensör, telefonun yan tarafındaki daha kısa olan düğmedir."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Parmak izi sensörü"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"kimlik doğrulaması yapın"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz girin"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri dön"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Geri dönmek için dokunmatik alanın herhangi bir yerinde üç parmağınızla sola veya sağa kaydırın."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Sağa ve sola hareket eden üç parmağın gösterildiği dokunmatik alan"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Geri hareketi animasyonunu gösteren cihaz ekranı"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 46d0619..2fc79e5 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Ви зупините запис контенту з додатка <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Ви зупините надсилання контенту з додатка <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Ви зупините трансляцію контенту з додатка <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Такі функції, як швидкий обмін і \"Знайти пристрій\", використовують Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Поділитись аудіо"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Надсилання аудіо"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"виберіть віджет"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"видалити віджет"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"розмістити вибраний віджет"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Змінити користувача"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"спадне меню"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Почати зараз"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Сповіщень немає"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Немає нових сповіщень"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Розблокуйте, щоб переглянути старіші"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Цим пристроєм керує батько або мати"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Цей пристрій належить вашій організації. Її адміністратор може відстежувати мережевий трафік"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Натисніть, щоб дізнатися більше"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Немає будильників"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"показати способи розблокування екрана"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Торкніться сканера відбитків пальців. Це менша кнопка на бічній панелі телефона."</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер відбитків пальців"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"пройти автентифікацію"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"відкрити пристрій"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 38bddf7..7d43c0a2 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"آپ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو ریکارڈ نہیں کر پائیں گے"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"آپ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کا اشتراک نہیں کر پائیں گے"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"آپ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو کاسٹ نہیں کر پائیں گے"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"فوری اشتراک اور \'میرا آلہ ڈھونڈیں\' جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"آڈیو کا اشتراک کریں"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"آڈیو کا اشتراک ہو رہا ہے"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"ویجیٹ منتخب کریں"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"ویجیٹ ہٹائیں"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"منتخب ویجیٹ رکھیں"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"صارف سوئچ کریں"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"پل ڈاؤن مینیو"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"ابھی شروع کریں"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"کوئی اطلاعات نہیں ہیں"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"کوئی نئی اطلاعات نہیں"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"تغیر پذیر اطلاعات آن ہیں"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"کم وقت میں متعدد اطلاعات ملنے پر آپ کا آلہ اب دو منٹ تک سکرین پر والیوم اور پوپ اپس کم کر دیتا ہے۔"</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"آف کریں"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"پرانی اطلاعات دیکھنے کیلئے غیر مقفل کریں"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"یہ آلہ آپ کے والدین کے زیر انتظام ہے"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"آپ کی تنظیم اس آلے کی مالک ہے اور نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے"</string>
@@ -1201,6 +1211,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"مزید معلومات کے لیے تھپتھپائیں"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"کوئی الارم سیٹ نہیں ہے"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"اسکرین لاک درج کریں"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"فنگر پرنٹ سینسر کو ٹچ کریں۔ یہ فون کے سائیڈ میں چھوٹا سا بٹن ہے۔"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"فنگر پرنٹ سینسر"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"تصدیق کریں"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"آلہ درج کریں"</string>
@@ -1359,6 +1370,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"واپس جانے کے لیے، ٹچ پیڈ پر کہیں بھی تین انگلیوں کی مدد سے دائیں یا بائیں سوائپ کریں۔"</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ٹچ پیڈ دائیں اور بائیں حرکت کرتی ہوئی تین انگلیاں دکھا رہا ہے"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"آلہ کی اسکرین پیچھے جانے کے اشارے کے لیے اینیمیشن دکھا رہی ہے"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d میں سے %1$d کا لیول"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 52ecbc3..825c507 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> yozib olinishini toʻxtatasiz"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ulashuvini toʻxtatasiz"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> translatsiyasini toʻxtatasiz"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tezkor ulashuv va Qurilmamni top kabi funksiyalar Bluetooth ishlatadi"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioni ulashish"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audio ulashuvi yoniq"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidjet tanlash"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"vidjetni olib tashlash"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"tanlangan vidjetni joylash"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Foydalanuvchini almashtirish"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"tortib tushiriladigan menyu"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Yangi bildirishoma yoʻq"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eskilarini koʻrish uchun qulfni yeching"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Bu qurilmani ota-onangiz boshqaradi"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Bu qurilma tashkilotingizga tegishli va tarmoq trafigi tashkilotingiz tomonidan kuzatilishi mumkin"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Batafsil axborot olish uchun bosing"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Signal sozlanmagan"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"ekran qulfini kiriting"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Barmoq izi skaneriga tegining. Bu – telefonning yon tomonidagi qisqa tugma"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Barmoq izi skaneri"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatsiya"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"qurilmani ochish"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 041da41..bf0126c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Bạn sẽ dừng ghi âm nội dung của <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Bạn sẽ dừng chia sẻ nội dung của <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Bạn sẽ dừng truyền nội dung của <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Các tính năng như Chia sẻ nhanh và Tìm thiết bị của tôi đều sử dụng Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sẽ bật vào sáng mai"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Chia sẻ âm thanh"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Đang chia sẻ âm thanh"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"chọn tiện ích"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"xoá tiện ích"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"đặt tiện ích đã chọn vào vị trí"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Chuyển đổi người dùng"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"trình đơn kéo xuống"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Bắt đầu ngay"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Không có thông báo nào"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Không có thông báo mới"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Mở khoá để xem thông báo cũ"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Thiết bị này do cha mẹ của bạn quản lý"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Tổ chức của bạn sở hữu thiết bị này và có thể giám sát lưu lượng truy cập mạng"</string>
@@ -780,7 +793,7 @@
<string name="keyboard_shortcut_join" msgid="3578314570034512676">"hoặc"</string>
<string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Xoá cụm từ tìm kiếm"</string>
<string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Phím tắt"</string>
- <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Lối tắt tìm kiếm"</string>
+ <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Tìm lối tắt"</string>
<string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Không tìm thấy lối tắt"</string>
<string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Hệ thống"</string>
<string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Đầu vào"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Nhấn để biết thêm thông tin"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Chưa đặt chuông báo"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"nhập phương thức mở khoá màn hình"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Hãy chạm vào cảm biến vân tay. Đó là nút ngắn ở cạnh bên của điện thoại"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Cảm biến vân tay"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"xác thực"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"truy cập thiết bị"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9df7a50..c47248d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"将停止录制<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>的内容"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"将停止分享<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>的内容"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"将停止投放<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>的内容"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"快速分享、查找我的设备等功能会使用蓝牙"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音频"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音频"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"选择微件"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除微件"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所选微件"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉菜单"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"没有通知"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"没有新通知"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解锁即可查看旧通知"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"此设备由您的家长管理"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"贵单位拥有此设备,且可能会监控网络流量"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"点按即可了解详情"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未设置闹钟"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"输入屏幕解锁信息"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"请轻触指纹传感器,即手机侧面较短的那个按钮"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指纹传感器"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"身份验证"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"进入设备"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 9467b9b..bce5777 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -188,7 +188,7 @@
</string-array>
<string-array name="tile_states_hearing_devices">
<item msgid="1235334096484287173">"不可用"</item>
- <item msgid="3079622119444911877">"关闭"</item>
+ <item msgid="3079622119444911877">"已关闭"</item>
<item msgid="3028994095749238254">"开启"</item>
</string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index fdb7759..b448943 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"你將停止錄影「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"你將停止分享「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"你將停止投放「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速共享」和「尋找我的裝置」等功能都會使用藍牙"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"揀小工具"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"沒有通知"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"沒有新通知"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解鎖即可查看舊通知"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"此裝置由你的家長管理"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"你的機構擁有此裝置,並可能會監察網絡流量"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"輕按即可瞭解詳情"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未設定鬧鐘"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"輸入螢幕鎖定憑證"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"輕輕掂下指紋感應器,即係手機側邊短啲嗰粒掣"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋感應器"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index cbeb38a..2a8430ae3 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"系統將停止錄製「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」的內容"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"系統將停止分享「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」的內容"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"系統將停止投放「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」的內容"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -382,7 +383,7 @@
<string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
<string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
<string name="qs_record_issue_bug_report" msgid="8229031766918650079">"錯誤報告"</string>
- <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受到影響?"</string>
+ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪方面的裝置使用體驗受到影響?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string>
<string name="performance" msgid="6552785217174378320">"效能"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"選取小工具"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會刪除。"</string>
@@ -542,6 +549,12 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"立即開始"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"沒有通知"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"沒有新通知"</string>
+ <!-- no translation found for adaptive_notification_edu_hun_title (5720882373252389461) -->
+ <skip />
+ <!-- no translation found for adaptive_notification_edu_hun_text (4260536236101821273) -->
+ <skip />
+ <!-- no translation found for go_to_adaptive_notification_settings (2423690125178298479) -->
+ <skip />
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解鎖即可查看舊通知"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"這個裝置是由你的家長管理"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"貴機構擁有這部裝置,而且可能會監控網路流量"</string>
@@ -815,7 +828,7 @@
<string name="input_switch_input_language_next" msgid="3782155659868227855">"切換到下一個語言"</string>
<string name="input_switch_input_language_previous" msgid="6043341362202336623">"切換到上一個語言"</string>
<string name="input_access_emoji" msgid="8105642858900406351">"存取表情符號"</string>
- <string name="input_access_voice_typing" msgid="7291201476395326141">"存取語音輸入內容"</string>
+ <string name="input_access_voice_typing" msgid="7291201476395326141">"使用語音輸入功能"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"應用程式"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Google 助理"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"瀏覽器"</string>
@@ -1197,6 +1210,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"輕觸即可瞭解詳情"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未設定鬧鐘"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"輸入螢幕鎖定憑證"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"請輕觸指紋感應器,也就是手機側邊較短的按鈕"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋感應器"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
@@ -1355,6 +1369,14 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <!-- no translation found for touchpad_back_gesture_action_title (7199067250654332735) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_guidance (4222430588599527272) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_animation_content_description (2646107450922379918) -->
+ <skip />
+ <!-- no translation found for touchpad_back_gesture_screen_animation_content_description (4036267494237748710) -->
+ <skip />
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 2117568..7e974bf 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -129,18 +129,21 @@
<skip />
<!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
<skip />
+ <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"Uzoyeka ukurekhoda <b>i-<xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
<skip />
<!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
<skip />
+ <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"Uzoyeka ukwaba <b>i-<xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
<skip />
<!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
<skip />
+ <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"Uzoyeka ukusakaza <b>i-<xliff:g id="APP_NAME">%1$s</xliff:g></b>"</string>
<!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
<skip />
<!-- no translation found for close_dialog_button (4749497706540104133) -->
@@ -303,10 +306,8 @@
<skip />
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Izakhi ezifana nokuthi Ukwabelana Ngokushesha kanye nokuthi Thola Idivayisi Yami zisebenzisa i-Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string>
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
- <skip />
- <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
- <skip />
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Yabelana ngomsindo"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Yabelana ngomsindo"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -489,6 +490,12 @@
<string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"khetha iwijethi"</string>
<string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"susa iwijethi"</string>
<string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"beka iwijethi ekhethiwe"</string>
+ <!-- no translation found for communal_widgets_disclaimer_title (1150954395585308868) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_text (1423545475160506349) -->
+ <skip />
+ <!-- no translation found for communal_widgets_disclaimer_button (4423059765740780753) -->
+ <skip />
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Shintsha umsebenzisi"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"imenyu yokudonsela phansi"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wonke ama-app nedatha kulesi sikhathi azosuswa."</string>
@@ -542,6 +549,9 @@
<string name="media_projection_action_text" msgid="3634906766918186440">"Qala manje"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Azikho izaziso"</string>
<string name="no_unseen_notif_text" msgid="395512586119868682">"Azikho izaziso ezintsha"</string>
+ <string name="adaptive_notification_edu_hun_title" msgid="5720882373252389461">"Izaziso zokuzijwayeza zivuliwe"</string>
+ <string name="adaptive_notification_edu_hun_text" msgid="4260536236101821273">"Idivayisi yakho manje yehlisa ivolumu futhi yehlisa okwesikhashana esikrinini kuze kufike emizuzwini emibili lapho uthola izaziso eziningi ngesikhathi esifushane."</string>
+ <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vala"</string>
<string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Vula ukuze ubone izaziso ezindala"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Le divayisi iphethwe ngumzali wakho"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Inhlangano yakho ingumnikazi wale divayisi futhi ingaqapha ithrafikhi yenethiwekhi"</string>
@@ -1197,6 +1207,7 @@
<string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Thepha ukuze uthole olunye ulwazi"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Akukho alamu esethiwe"</string>
<string name="accessibility_bouncer" msgid="5896923685673320070">"faka ukukhiya isikrini"</string>
+ <string name="accessibility_side_fingerprint_indicator_label" msgid="1673807833352363712">"Thinta inzwa yesigxivizo somunwe Inkinobho emfushane ehlangothini lwefoni"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Inzwa yesigxivizo somunwe"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"gunyaza"</string>
<string name="accessibility_enter_hint" msgid="2617864063504824834">"faka idivayisi"</string>
@@ -1355,6 +1366,10 @@
<skip />
<!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
<skip />
+ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Buyela emuva"</string>
+ <string name="touchpad_back_gesture_guidance" msgid="4222430588599527272">"Ukuze ubuyele emuva, swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu noma yikuphi ephedini yokuthinta."</string>
+ <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Iphedi yokuthinta ebonisa iminwe emithathu iya kwesokudla nakwesokunxele"</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Isikrini sedivayisi esibonisa opopayi bokuthinta kwasemuva"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index ba59c2f9..b3d3021 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -18,7 +18,6 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <drawable name="notification_number_text_color">#ffffffff</drawable>
<drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
<color name="system_bar_background_opaque">#ff000000</color>
<color name="system_bar_background_transparent">#00000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f8762f0..80b9ec7 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -25,9 +25,6 @@
(package/class) -->
<string name="config_recentsComponent" translatable="false">com.android.systemui.recents.OverviewProxyRecentsImpl</string>
- <!-- Whether or not we show the number in the bar. -->
- <bool name="config_statusBarShowNumber">false</bool>
-
<!-- For how long the lock screen can be on before the display turns off. -->
<integer name="config_lockScreenDisplayTimeout">10000</integer>
@@ -1049,4 +1046,7 @@
<!-- The width of the shortcut helper container, as a fraction of the screen's width. -->
<item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">1.0</item>
+
+ <!-- Only applicable for dual shade - Allow Notifications/QS shade to anchor to the bottom. -->
+ <bool name="config_dualShadeAlignedToBottom">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cf91326..40bdc3e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -810,6 +810,8 @@
<dimen name="keyguard_smartspace_top_offset">12dp</dimen>
<!-- The amount to translate lockscreen elements on the GONE->AOD transition -->
<dimen name="keyguard_enter_from_top_translation_y">-100dp</dimen>
+ <!-- The amount to translate lockscreen elements on the GONE->AOD transition, on device fold -->
+ <dimen name="keyguard_enter_from_side_translation_x">-100dp</dimen>
<dimen name="notification_scrim_corner_radius">32dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8381812..2f61b12 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -269,6 +269,7 @@
<string name="screenshot_detected_multiple_template"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> and other open apps detected this screenshot.</string>
<!-- Add to note button used in App Clips flow to return the saved screenshot image to notes app. [CHAR LIMIT=NONE] -->
<string name="app_clips_save_add_to_note">Add to note</string>
+ <string name="backlinks_include_link">Include link</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_title">Screen Recorder</string>
@@ -328,6 +329,8 @@
<!-- Button to stop a screen recording [CHAR LIMIT=35] -->
<string name="screenrecord_stop_dialog_button">Stop recording</string>
+ <!-- Content description for the status bar chip shown to the user when they're sharing their screen to another app on the device [CHAR LIMIT=NONE] -->
+ <string name="share_to_app_chip_accessibility_label">Sharing screen</string>
<!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] -->
<string name="share_to_app_stop_dialog_title">Stop sharing screen?</string>
<!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] -->
@@ -337,6 +340,8 @@
<!-- Button to stop screen sharing [CHAR LIMIT=35] -->
<string name="share_to_app_stop_dialog_button">Stop sharing</string>
+ <!-- Content description for the status bar chip shown to the user when they're casting their screen to a different device [CHAR LIMIT=NONE] -->
+ <string name="cast_to_other_device_chip_accessibility_label">Casting screen</string>
<!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] -->
<string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string>
<!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] -->
@@ -441,6 +446,8 @@
<string name="biometric_dialog_tap_confirm_with_face_alt_2">Face recognized. Press to continue.</string>
<!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]-->
<string name="biometric_dialog_tap_confirm_with_face_alt_3">Face recognized. Press the unlock icon to continue.</string>
+ <!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication with SFPS [CHAR LIMIT=60]-->
+ <string name="biometric_dialog_tap_confirm_with_face_sfps">Unlocked by face. Tap to continue.</string>
<!-- Talkback string when a biometric is authenticated [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_authenticated">Authenticated</string>
<!-- Talkback string when a canceling authentication [CHAR LIMIT=NONE] -->
@@ -1222,6 +1229,12 @@
<string name="accessibility_action_label_remove_widget">remove widget</string>
<!-- Label for accessibility action to place a widget in edit mode after selecting move widget. [CHAR LIMIT=NONE] -->
<string name="accessibility_action_label_place_widget">place selected widget</string>
+ <!-- Title shown above information regarding lock screen widgets. [CHAR LIMIT=50] -->
+ <string name="communal_widgets_disclaimer_title">Lock screen widgets</string>
+ <!-- Information about lock screen widgets presented to the user. [CHAR LIMIT=NONE] -->
+ <string name="communal_widgets_disclaimer_text">To open an app using a widget, you\u2019ll need to verify it\u2019s you. Also, keep in mind that anyone can view them, even when your tablet\u2019s locked. Some widgets may not have been intended for your lock screen and may be unsafe to add here.</string>
+ <!-- Button for user to verify they understand the information presented. [CHAR LIMIT=50] -->
+ <string name="communal_widgets_disclaimer_button">Got it</string>
<!-- Related to user switcher --><skip/>
@@ -1380,6 +1393,15 @@
<!-- Text which is shown in the expanded notification shade when there are currently no notifications visible that the user hasn't already seen. [CHAR LIMIT=30] -->
<string name="no_unseen_notif_text">No new notifications</string>
+ <!-- Title of heads up notification for adaptive notifications user education. [CHAR LIMIT=30] -->
+ <string name="adaptive_notification_edu_hun_title">Adaptive notifications is on</string>
+
+ <!-- Text of heads up notification for adaptive notifications user education. [CHAR LIMIT=100] -->
+ <string name="adaptive_notification_edu_hun_text">Your device now lowers the volume and reduces pop-ups on the screen for up to two minutes when you receive many notifications in a short time span.</string>
+
+ <!-- Action label for going to adaptive notification settings [CHAR LIMIT=20] -->
+ <string name="go_to_adaptive_notification_settings">Turn off</string>
+
<!-- Text which is shown in the locked notification shade when there are currently no notifications, but if the user were to unlock, notifications would appear. [CHAR LIMIT=40] -->
<string name="unlock_to_see_notif_text">Unlock to see older notifications</string>
@@ -3526,10 +3548,26 @@
shows the user which keyboard shortcuts they can use. The "System" shortcuts are for
example "Take a screenshot" or "Go back". [CHAR LIMIT=NONE] -->
<string name="shortcut_helper_category_system">System</string>
+ <!-- Title of the keyboard shortcut helper category "System controls". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "System controls" shortcuts
+ are for example "Go to home screen" or "App apps search". [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_category_system_controls">System controls</string>
+ <!-- Title of the keyboard shortcut helper category "System apps". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "System apps" shortcuts
+ are for example "Settings" or "Take a note". [CHAR LIMIT=NONE] -->
+ <string name="shortcut_helper_category_system_apps">System apps</string>
<!-- Title of the keyboard shortcut helper category "Multitasking". The helper is a component
that shows the user which keyboard shortcuts they can use. The "Multitasking" shortcuts are
for example "Enter split screen". [CHAR LIMIT=NONE] -->
<string name="shortcut_helper_category_multitasking">Multitasking</string>
+ <!-- Title of the keyboard shortcut helper category "Recent apps". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "Recent apps" shortcuts are
+ for example "Cycle through recent apps". [CHAR LIMIT=NONE] -->
+ <string name="shortcutHelper_category_recent_apps">Recent apps</string>
+ <!-- Title of the keyboard shortcut helper category "Split screen". The helper is a component
+ that shows the user which keyboard shortcuts they can use. The "Split screen" shortcuts are
+ for example "Move current app to left split". [CHAR LIMIT=NONE] -->
+ <string name="shortcutHelper_category_split_screen">Split screen</string>
<!-- Title of the keyboard shortcut helper category "Input". The helper is a component
that shows the user which keyboard shortcuts they can use. The "Input" shortcuts are
the ones provided by the keyboard. Examples are "Access emoji" or "Switch to next language"
@@ -3577,6 +3615,12 @@
<string name="touchpad_tutorial_action_key_button">Action key</string>
<!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] -->
<string name="touchpad_tutorial_done_button">Done</string>
+ <!-- Touchpad back gesture action name in tutorial [CHAR LIMIT=NONE] -->
+ <string name="touchpad_back_gesture_action_title">Go back</string>
+ <!-- Touchpad back gesture guidance in gestures tutorial [CHAR LIMIT=NONE] -->
+ <string name="touchpad_back_gesture_guidance">To go back, swipe left or right using three fingers anywhere on the touchpad.</string>
+ <string name="touchpad_back_gesture_animation_content_description">Touchpad showing three fingers moving right and left</string>
+ <string name="touchpad_back_gesture_screen_animation_content_description">Device screen showing animation for back gesture</string>
<!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
<string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index d5bc10a..c00007b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -22,7 +22,7 @@
import android.view.MotionEvent;
import com.android.systemui.shared.recents.ISystemUiProxy;
-// Next ID: 29
+// Next ID: 34
oneway interface IOverviewProxy {
void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -83,6 +83,11 @@
void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
/**
+ * Sent when {@link TaskbarDelegate#onTransitionModeUpdated} is called.
+ */
+ void onTransitionModeUpdated(int barMode, boolean checkBarModes) = 21;
+
+ /**
* Sent when the desired dark intensity of the nav buttons has changed
*/
void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
@@ -101,4 +106,30 @@
* Sent when the task bar stash state is toggled.
*/
void onTaskbarToggled() = 27;
+
+ /**
+ * Sent when the wallpaper visibility is updated.
+ */
+ void updateWallpaperVisibility(int displayId, boolean visible) = 29;
+
+ /**
+ * Sent when {@link TaskbarDelegate#checkNavBarModes} is called.
+ */
+ void checkNavBarModes() = 30;
+
+ /**
+ * Sent when {@link TaskbarDelegate#finishBarAnimations} is called.
+ */
+ void finishBarAnimations() = 31;
+
+ /**
+ * Sent when {@link TaskbarDelegate#touchAutoDim} is called. {@param reset} is true, when auto
+ * dim is reset after a timeout.
+ */
+ void touchAutoDim(boolean reset) = 32;
+
+ /**
+ * Sent when {@link TaskbarDelegate#transitionTo} is called.
+ */
+ void transitionTo(int barMode, boolean animate) = 33;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index f06e333..edf855f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -246,6 +246,12 @@
public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
+ @ViewDebug.ExportedProperty(category="recents")
+ public boolean isVisible;
+
+ @ViewDebug.ExportedProperty(category = "recents")
+ public boolean isMinimized;
+
public Task() {
// Do nothing
}
@@ -279,6 +285,8 @@
lastSnapshotData.set(other.lastSnapshotData);
positionInParent = other.positionInParent;
appBounds = other.appBounds;
+ isVisible = other.isVisible;
+ isMinimized = other.isMinimized;
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index 3cb5bc7..ea73216 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -10,7 +10,6 @@
import android.graphics.RectF;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.wm.shell.util.SplitBounds;
/**
* Utility class to position the thumbnail in the TaskView
@@ -35,8 +34,6 @@
private final Matrix mMatrix = new Matrix();
private boolean mIsOrientationChanged;
- private SplitBounds mSplitBounds;
- private int mDesiredStagePosition;
public Matrix getMatrix() {
return mMatrix;
@@ -50,11 +47,6 @@
return mIsOrientationChanged;
}
- public void setSplitBounds(SplitBounds splitBounds, int desiredStagePosition) {
- mSplitBounds = splitBounds;
- mDesiredStagePosition = desiredStagePosition;
- }
-
/**
* Updates the matrix based on the provided parameters
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 660f0db..2eac393 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -53,6 +53,9 @@
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -142,12 +145,14 @@
};
private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() {
+ @WorkerThread
@Override
public void onRotationChanged(final int rotation) {
+ @Nullable Boolean rotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
// We need this to be scheduled as early as possible to beat the redrawing of
// window in response to the orientation change.
mMainThreadHandler.postAtFrontOfQueue(() -> {
- onRotationWatcherChanged(rotation);
+ onRotationWatcherChanged(rotation, rotationLocked);
});
}
};
@@ -281,8 +286,8 @@
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
}
- public void setRotationLockedAtAngle(int rotationSuggestion, String caller) {
- final Boolean isLocked = isRotationLocked();
+ public void setRotationLockedAtAngle(
+ @Nullable Boolean isLocked, int rotationSuggestion, String caller) {
if (isLocked == null) {
// Ignore if we can't read the setting for the current user
return;
@@ -291,21 +296,6 @@
/* rotation= */ rotationSuggestion, caller);
}
- /**
- * @return whether rotation is currently locked, or <code>null</code> if the setting couldn't
- * be read
- */
- public Boolean isRotationLocked() {
- try {
- return RotationPolicy.isRotationLocked(mContext);
- } catch (SecurityException e) {
- // TODO(b/279561841): RotationPolicy uses the current user to resolve the setting which
- // may change before the rotation watcher can be unregistered
- Log.e(TAG, "Failed to get isRotationLocked", e);
- return null;
- }
- }
-
public void setRotateSuggestionButtonState(boolean visible) {
setRotateSuggestionButtonState(visible, false /* force */);
}
@@ -469,7 +459,7 @@
* Called when the rotation watcher rotation changes, either from the watcher registered
* internally in this class, or a signal propagated from NavBarHelper.
*/
- public void onRotationWatcherChanged(int rotation) {
+ public void onRotationWatcherChanged(int rotation, @Nullable Boolean isRotationLocked) {
if (!mListenersRegistered) {
// Ignore if not registered
return;
@@ -477,17 +467,16 @@
// If the screen rotation changes while locked, potentially update lock to flow with
// new screen rotation and hide any showing suggestions.
- Boolean rotationLocked = isRotationLocked();
- if (rotationLocked == null) {
+ if (isRotationLocked == null) {
// Ignore if we can't read the setting for the current user
return;
}
// The isVisible check makes the rotation button disappear when we are not locked
// (e.g. for tabletop auto-rotate).
- if (rotationLocked || mRotationButton.isVisible()) {
+ if (isRotationLocked || mRotationButton.isVisible()) {
// Do not allow a change in rotation to set user rotation when docked.
- if (shouldOverrideUserLockPrefs(rotation) && rotationLocked && !mDocked) {
- setRotationLockedAtAngle(rotation, /* caller= */
+ if (shouldOverrideUserLockPrefs(rotation) && isRotationLocked && !mDocked) {
+ setRotationLockedAtAngle(true, rotation, /* caller= */
"RotationButtonController#onRotationWatcherChanged");
}
setRotateSuggestionButtonState(false /* visible */, true /* forced */);
@@ -592,7 +581,8 @@
private void onRotateSuggestionClick(View v) {
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();
- setRotationLockedAtAngle(mLastRotationSuggestion,
+ setRotationLockedAtAngle(
+ RotationPolicyUtil.isRotationLocked(mContext), mLastRotationSuggestion,
/* caller= */ "RotationButtonController#onRotateSuggestionClick");
Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion);
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationPolicyUtil.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationPolicyUtil.kt
new file mode 100644
index 0000000..eac4a10
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationPolicyUtil.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.shared.rotation
+
+import android.content.Context
+import android.util.Log
+import com.android.internal.view.RotationPolicy
+
+class RotationPolicyUtil {
+ companion object {
+ /**
+ * Recommend to be called on bg thread, or reuse the results. It's because
+ * [RotationPolicy.isRotationLocked] may make a binder call to query settings.
+ *
+ * @return whether rotation is currently locked, or <code>null</code> if the setting
+ * couldn't be read
+ */
+ @JvmStatic
+ fun isRotationLocked(context: Context): Boolean? {
+ try {
+ return RotationPolicy.isRotationLocked(context)
+ } catch (e: SecurityException) {
+ // TODO(b/279561841): RotationPolicy uses the current user to resolve the setting
+ // which may change before the rotation watcher can be unregistered
+ Log.e("RotationPolicy", "Failed to get isRotationLocked", e)
+ return null
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/shared/src/com/android/systemui/shared/statusbar/phone/BarTransitions.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/statusbar/phone/BarTransitions.java
index f62a79f..c123306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/statusbar/phone/BarTransitions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shared.statusbar.phone;
+import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -34,8 +36,6 @@
import android.view.View;
import com.android.app.animation.Interpolators;
-import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -44,6 +44,11 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_COLORS = false;
+ @ColorInt
+ private static final int SYSTEM_BAR_BACKGROUND_OPAQUE = Color.BLACK;
+ @ColorInt
+ private static final int SYSTEM_BAR_BACKGROUND_TRANSPARENT = Color.TRANSPARENT;
+
public static final int MODE_TRANSPARENT = 0;
public static final int MODE_SEMI_TRANSPARENT = 1;
public static final int MODE_TRANSLUCENT = 2;
@@ -183,11 +188,11 @@
mTransparent = 0x2f0000ff;
mWarning = 0xffff0000;
} else {
- mOpaque = context.getColor(R.color.system_bar_background_opaque);
+ mOpaque = SYSTEM_BAR_BACKGROUND_OPAQUE;
mSemiTransparent = context.getColor(
com.android.internal.R.color.system_bar_background_semi_transparent);
- mTransparent = context.getColor(R.color.system_bar_background_transparent);
- mWarning = Utils.getColorAttrDefaultColor(context, android.R.attr.colorError);
+ mTransparent = SYSTEM_BAR_BACKGROUND_TRANSPARENT;
+ mWarning = getColorAttrDefaultColor(context, android.R.attr.colorError, 0);
}
mGradient = context.getDrawable(gradientResourceId);
}
@@ -226,7 +231,7 @@
@Override
public void setTint(int color) {
PorterDuff.Mode targetMode = mTintFilter == null ? Mode.SRC_IN :
- mTintFilter.getMode();
+ mTintFilter.getMode();
if (mTintFilter == null || mTintFilter.getColor() != color) {
mTintFilter = new PorterDuffColorFilter(color, targetMode);
}
@@ -304,10 +309,13 @@
Interpolators.LINEAR.getInterpolation(t), 1));
mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v));
mColor = Color.argb(
- (int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)),
- (int)(v * Color.red(targetColor) + Color.red(mColorStart) * (1 - v)),
- (int)(v * Color.green(targetColor) + Color.green(mColorStart) * (1 - v)),
- (int)(v * Color.blue(targetColor) + Color.blue(mColorStart) * (1 - v)));
+ (int) (v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1
+ - v)),
+ (int) (v * Color.red(targetColor) + Color.red(mColorStart) * (1 - v)),
+ (int) (v * Color.green(targetColor) + Color.green(mColorStart) * (1
+ - v)),
+ (int) (v * Color.blue(targetColor) + Color.blue(mColorStart) * (1
+ - v)));
}
}
if (mGradientAlpha > 0) {
@@ -332,4 +340,13 @@
}
}
}
+
+ /** Get color styled attribute {@code attr}, default to {@code defValue} if not found. */
+ @ColorInt
+ public static int getColorAttrDefaultColor(Context context, int attr, @ColorInt int defValue) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {attr});
+ @ColorInt int colorAccent = ta.getColor(0, defValue);
+ ta.recycle();
+ return colorAccent;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 5e76801..bf1f93f 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -28,6 +28,7 @@
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
@@ -86,6 +87,12 @@
* Trigger ActiveUnlock when the assistant is triggered.
*/
ASSISTANT,
+ /**
+ * Trigger ActiveUnlock on legacy unlock intents. This includes tapping on the empty space
+ * of the notification shadse when face auth is enrolled and re-trying face auth on the
+ * primary bouncer.
+ */
+ UNLOCK_INTENT_LEGACY,
}
/**
@@ -99,6 +106,7 @@
}
private var requestActiveUnlockOnWakeup = false
+ private var requestActiveUnlockOnUnlockIntentLegacy = false
private var requestActiveUnlockOnUnlockIntent = false
private var requestActiveUnlockOnBioFail = false
@@ -110,6 +118,8 @@
private val settingsObserver = object : ContentObserver(handler) {
private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
+ private val unlockIntentLegacyUri =
+ secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY)
private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)
@@ -164,6 +174,15 @@
ACTIVE_UNLOCK_ON_WAKE, 0, selectedUserInteractor.getSelectedUserId()) == 1
}
+ if (selfChange || uris.contains(unlockIntentLegacyUri)) {
+ requestActiveUnlockOnUnlockIntentLegacy =
+ secureSettings.getIntForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
+ 0,
+ selectedUserInteractor.getSelectedUserId()
+ ) == 1
+ }
+
if (selfChange || uris.contains(unlockIntentUri)) {
requestActiveUnlockOnUnlockIntent = secureSettings.getIntForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0,
@@ -257,7 +276,7 @@
*/
fun isActiveUnlockEnabled(): Boolean {
return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
- requestActiveUnlockOnBioFail
+ requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntentLegacy
}
/**
@@ -299,15 +318,18 @@
fun shouldAllowActiveUnlockFromOrigin(requestOrigin: ActiveUnlockRequestOrigin): Boolean {
return when (requestOrigin) {
ActiveUnlockRequestOrigin.WAKE -> requestActiveUnlockOnWakeup
-
+ ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY ->
+ requestActiveUnlockOnUnlockIntentLegacy
ActiveUnlockRequestOrigin.UNLOCK_INTENT ->
- requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
- (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
-
+ requestActiveUnlockOnUnlockIntent ||
+ requestActiveUnlockOnUnlockIntentLegacy ||
+ requestActiveUnlockOnWakeup ||
+ (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
ActiveUnlockRequestOrigin.BIOMETRIC_FAIL ->
- requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
- requestActiveUnlockOnWakeup
-
+ requestActiveUnlockOnBioFail ||
+ requestActiveUnlockOnUnlockIntentLegacy ||
+ requestActiveUnlockOnUnlockIntent ||
+ requestActiveUnlockOnWakeup
ActiveUnlockRequestOrigin.ASSISTANT -> isActiveUnlockEnabled()
}
}
@@ -345,6 +367,9 @@
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Settings:")
pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
+ pw.println(
+ " requestActiveUnlockOnUnlockIntentLegacy=$requestActiveUnlockOnUnlockIntentLegacy"
+ )
pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 70465bc..4217820 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -1097,7 +1097,8 @@
int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
AnimatorSet anims = new AnimatorSet();
- ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
+ ObjectAnimator yAnim = ObjectAnimator.ofFloat(mViewFlipper, View.TRANSLATION_Y,
+ yTranslation);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
0f);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 42838ae..428cd0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -351,7 +351,7 @@
mDeviceEntryFaceAuthInteractor.onSwipeUpOnBouncer();
if (mDeviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
mUpdateMonitor.requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY,
"swipeUpOnBouncer");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
index 004d5db..c2c5277 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
@@ -42,4 +42,7 @@
fun accessibilityQsShortcutsRepository(
impl: AccessibilityQsShortcutsRepositoryImpl
): AccessibilityQsShortcutsRepository
+
+ @Binds
+ fun magnification(impl: MagnificationImpl): Magnification
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 3c4c003..3c0ac9a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -56,7 +56,7 @@
import java.util.concurrent.Executor;
import java.util.function.Supplier;
-class FullscreenMagnificationController implements ComponentCallbacks {
+public class FullscreenMagnificationController implements ComponentCallbacks {
private static final String TAG = "FullscreenMagnificationController";
private final Context mContext;
@@ -87,7 +87,7 @@
};
private final long mLongAnimationTimeMs;
- FullscreenMagnificationController(
+ public FullscreenMagnificationController(
@UiContext Context context,
@Main Handler handler,
@Main Executor executor,
@@ -157,7 +157,7 @@
* there is an activation change.
*/
@UiThread
- void onFullscreenMagnificationActivationChanged(boolean activated) {
+ public void onFullscreenMagnificationActivationChanged(boolean activated) {
final boolean changed = (mFullscreenMagnificationActivated != activated);
if (changed) {
mFullscreenMagnificationActivated = activated;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index e22a4e4..48e5f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -16,618 +16,54 @@
package com.android.systemui.accessibility;
-import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
-import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-
-import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
-
import android.annotation.MainThread;
import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-import android.window.InputTransferToken;
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.util.settings.SecureSettings;
-import java.io.PrintWriter;
-import java.util.concurrent.Executor;
-import java.util.function.Supplier;
-
-import javax.inject.Inject;
-
-/**
- * Class to handle the interaction with
- * {@link com.android.server.accessibility.AccessibilityManagerService}. It invokes
- * {@link AccessibilityManager#setMagnificationConnection(IMagnificationConnection)}
- * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
- */
-@SysUISingleton
-public class Magnification implements CoreStartable, CommandQueue.Callbacks {
- private static final String TAG = "Magnification";
-
- @VisibleForTesting static final int DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS = 300;
- private static final int MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL = 1;
-
- private final ModeSwitchesController mModeSwitchesController;
- private final Context mContext;
- private final Handler mHandler;
- private final Executor mExecutor;
- private final AccessibilityManager mAccessibilityManager;
- private final CommandQueue mCommandQueue;
- private final OverviewProxyService mOverviewProxyService;
- private final DisplayTracker mDisplayTracker;
- private final AccessibilityLogger mA11yLogger;
-
- private MagnificationConnectionImpl mMagnificationConnectionImpl;
- private SysUiState mSysUiState;
-
- @VisibleForTesting
- SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
-
- private static class WindowMagnificationControllerSupplier extends
- DisplayIdIndexSupplier<WindowMagnificationController> {
-
- private final Context mContext;
- private final Handler mHandler;
- private final WindowMagnifierCallback mWindowMagnifierCallback;
- private final SysUiState mSysUiState;
- private final SecureSettings mSecureSettings;
-
- WindowMagnificationControllerSupplier(Context context, Handler handler,
- WindowMagnifierCallback windowMagnifierCallback,
- DisplayManager displayManager, SysUiState sysUiState,
- SecureSettings secureSettings) {
- super(displayManager);
- mContext = context;
- mHandler = handler;
- mWindowMagnifierCallback = windowMagnifierCallback;
- mSysUiState = sysUiState;
- mSecureSettings = secureSettings;
- }
-
- @Override
- protected WindowMagnificationController createInstance(Display display) {
- final Context windowContext = mContext.createWindowContext(display,
- Flags.createWindowlessWindowMagnifier()
- ? TYPE_ACCESSIBILITY_OVERLAY
- : TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- /* options */ null);
- windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
-
- Supplier<SurfaceControlViewHost> scvhSupplier = () ->
- Flags.createWindowlessWindowMagnifier() ? new SurfaceControlViewHost(mContext,
- mContext.getDisplay(), new InputTransferToken(), TAG) : null;
-
- return new WindowMagnificationController(
- windowContext,
- mHandler,
- new WindowMagnificationAnimationController(windowContext),
- /* mirrorWindowControl= */ null,
- new SurfaceControl.Transaction(),
- mWindowMagnifierCallback,
- mSysUiState,
- mSecureSettings,
- scvhSupplier,
- new SfVsyncFrameCallbackProvider(),
- WindowManagerGlobal::getWindowSession);
- }
- }
-
- @VisibleForTesting
- DisplayIdIndexSupplier<WindowMagnificationController> mWindowMagnificationControllerSupplier;
-
- private static class FullscreenMagnificationControllerSupplier extends
- DisplayIdIndexSupplier<FullscreenMagnificationController> {
-
- private final Context mContext;
- private final Handler mHandler;
- private final Executor mExecutor;
- private final IWindowManager mIWindowManager;
-
- FullscreenMagnificationControllerSupplier(Context context,
- DisplayManager displayManager,
- Handler handler,
- Executor executor, IWindowManager iWindowManager) {
- super(displayManager);
- mContext = context;
- mHandler = handler;
- mExecutor = executor;
- mIWindowManager = iWindowManager;
- }
-
- @Override
- protected FullscreenMagnificationController createInstance(Display display) {
- final Context windowContext = mContext.createWindowContext(display,
- TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
- Supplier<SurfaceControlViewHost> scvhSupplier = () -> new SurfaceControlViewHost(
- mContext, mContext.getDisplay(), new InputTransferToken(), TAG);
- windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
- return new FullscreenMagnificationController(
- windowContext,
- mHandler,
- mExecutor,
- windowContext.getSystemService(AccessibilityManager.class),
- windowContext.getSystemService(WindowManager.class),
- mIWindowManager,
- scvhSupplier);
- }
- }
-
- @VisibleForTesting
- DisplayIdIndexSupplier<FullscreenMagnificationController>
- mFullscreenMagnificationControllerSupplier;
-
- private static class SettingsSupplier extends
- DisplayIdIndexSupplier<MagnificationSettingsController> {
-
- private final Context mContext;
- private final MagnificationSettingsController.Callback mSettingsControllerCallback;
- private final SecureSettings mSecureSettings;
-
- SettingsSupplier(Context context,
- MagnificationSettingsController.Callback settingsControllerCallback,
- DisplayManager displayManager,
- SecureSettings secureSettings) {
- super(displayManager);
- mContext = context;
- mSettingsControllerCallback = settingsControllerCallback;
- mSecureSettings = secureSettings;
- }
-
- @Override
- protected MagnificationSettingsController createInstance(Display display) {
- final Context windowContext = mContext.createWindowContext(display,
- TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
- windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
- return new MagnificationSettingsController(
- windowContext,
- new SfVsyncFrameCallbackProvider(),
- mSettingsControllerCallback,
- mSecureSettings);
- }
- }
-
- @VisibleForTesting
- DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
-
- @Inject
- public Magnification(Context context,
- @Main Handler mainHandler, @Main Executor executor,
- CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- SysUiState sysUiState, OverviewProxyService overviewProxyService,
- SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger,
- IWindowManager iWindowManager) {
- this(context, mainHandler.getLooper(), executor, commandQueue,
- modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
- displayTracker, displayManager, a11yLogger, iWindowManager);
- }
-
- @VisibleForTesting
- public Magnification(Context context, Looper looper, @Main Executor executor,
- CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
- SysUiState sysUiState, OverviewProxyService overviewProxyService,
- SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger,
- IWindowManager iWindowManager) {
- mContext = context;
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(@NonNull Message msg) {
- if (msg.what == MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL) {
- showMagnificationButtonInternal(msg.arg1, msg.arg2);
- }
- }
- };
- mExecutor = executor;
- mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mCommandQueue = commandQueue;
- mModeSwitchesController = modeSwitchesController;
- mSysUiState = sysUiState;
- mOverviewProxyService = overviewProxyService;
- mDisplayTracker = displayTracker;
- mA11yLogger = a11yLogger;
- mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
- mHandler, mWindowMagnifierCallback,
- displayManager, sysUiState, secureSettings);
- mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
- context, displayManager, mHandler, mExecutor, iWindowManager);
- mMagnificationSettingsSupplier = new SettingsSupplier(context,
- mMagnificationSettingsControllerCallback, displayManager, secureSettings);
-
- mModeSwitchesController.setClickListenerDelegate(
- displayId -> mHandler.post(() -> {
- toggleSettingsPanelVisibility(displayId);
- }));
- }
-
- @Override
- public void start() {
- mCommandQueue.addCallback(this);
- mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
- @Override
- public void onConnectionChanged(boolean isConnected) {
- if (isConnected) {
- updateSysUiStateFlag();
- }
- }
- });
- }
-
- private void updateSysUiStateFlag() {
- //TODO(b/187510533): support multi-display once SysuiState supports it.
- final WindowMagnificationController controller =
- mWindowMagnificationControllerSupplier.valueAt(
- mDisplayTracker.getDefaultDisplayId());
- if (controller != null) {
- controller.updateSysUIStateFlag();
- } else {
- // The instance is initialized when there is an IPC request. Considering
- // self-crash cases, we need to reset the flag in such situation.
- mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
- .commitUpdate(mDisplayTracker.getDefaultDisplayId());
- }
- }
+/** Interface for managing magnification connection calls from system process. */
+public interface Magnification extends CoreStartable {
@MainThread
- void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
- @Nullable IRemoteMagnificationAnimationCallback callback) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
- magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
- }
- }
+ void enableWindowMagnification(
+ int displayId,
+ float scale,
+ float centerX,
+ float centerY,
+ float magnificationFrameOffsetRatioX,
+ float magnificationFrameOffsetRatioY,
+ @Nullable IRemoteMagnificationAnimationCallback callback);
@MainThread
- void setScaleForWindowMagnification(int displayId, float scale) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.setScale(scale);
- }
- }
+ void setScaleForWindowMagnification(int displayId, float scale);
@MainThread
- void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
- final WindowMagnificationController windowMagnificationcontroller =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationcontroller != null) {
- windowMagnificationcontroller.moveWindowMagnifier(offsetX, offsetY);
- }
- }
+ void moveWindowMagnifier(int displayId, float offsetX, float offsetY);
@MainThread
- void moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY,
- IRemoteMagnificationAnimationCallback callback) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
- callback);
- }
- }
+ void disableWindowMagnification(
+ int displayId, @Nullable IRemoteMagnificationAnimationCallback callback);
@MainThread
- void disableWindowMagnification(int displayId,
- @Nullable IRemoteMagnificationAnimationCallback callback) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.deleteWindowMagnification(callback);
- }
- }
+ void onFullscreenMagnificationActivationChanged(int displayId, boolean activated);
@MainThread
- void onFullscreenMagnificationActivationChanged(int displayId, boolean activated) {
- final FullscreenMagnificationController fullscreenMagnificationController =
- mFullscreenMagnificationControllerSupplier.get(displayId);
- if (fullscreenMagnificationController != null) {
- fullscreenMagnificationController.onFullscreenMagnificationActivationChanged(activated);
- }
- }
+ void showMagnificationButton(int displayId, int magnificationMode);
@MainThread
- void toggleSettingsPanelVisibility(int displayId) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- magnificationSettingsController.toggleSettingsPanelVisibility();
- }
- }
+ void removeMagnificationButton(int displayId);
@MainThread
- void hideMagnificationSettingsPanel(int displayId) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- magnificationSettingsController.closeMagnificationSettings();
- }
- }
-
- boolean isMagnificationSettingsPanelShowing(int displayId) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- return magnificationSettingsController.isMagnificationSettingsShowing();
- }
- return false;
- }
+ void setUserMagnificationScale(int userId, int displayId, float scale);
@MainThread
- void showMagnificationButton(int displayId, int magnificationMode) {
- if (Flags.delayShowMagnificationButton()) {
- if (mHandler.hasMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL)) {
- return;
- }
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(
- MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
- DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS);
- } else {
- showMagnificationButtonInternal(displayId, magnificationMode);
- }
- }
+ void hideMagnificationSettingsPanel(int displayId);
@MainThread
- private void showMagnificationButtonInternal(int displayId, int magnificationMode) {
- // not to show mode switch button if settings panel is already showing to
- // prevent settings panel be covered by the button.
- if (isMagnificationSettingsPanelShowing(displayId)) {
- return;
- }
- mModeSwitchesController.showButton(displayId, magnificationMode);
- }
-
- @MainThread
- void removeMagnificationButton(int displayId) {
- if (Flags.delayShowMagnificationButton()) {
- mHandler.removeMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL);
- }
- mModeSwitchesController.removeButton(displayId);
- }
-
- @MainThread
- void setUserMagnificationScale(int userId, int displayId, float scale) {
- SparseArray<Float> scales = mUsersScales.get(userId);
- if (scales == null) {
- scales = new SparseArray<>();
- mUsersScales.put(userId, scales);
- }
- if (scales.contains(displayId) && scales.get(displayId) == scale) {
- return;
- }
- scales.put(displayId, scale);
-
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- magnificationSettingsController.setMagnificationScale(scale);
- }
-
- @VisibleForTesting
- final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
- @Override
- public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
- }
- }
-
- @Override
- public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
- }
- }
-
- @Override
- public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onPerformScaleAction(
- displayId, scale, updatePersistence);
- }
- }
-
- @Override
- public void onAccessibilityActionPerformed(int displayId) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
- }
- }
-
- @Override
- public void onMove(int displayId) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onMove(displayId);
- }
- }
-
- @Override
- public void onClickSettingsButton(int displayId) {
- mHandler.post(() -> {
- toggleSettingsPanelVisibility(displayId);
- });
- }
- };
-
- @VisibleForTesting
- final MagnificationSettingsController.Callback mMagnificationSettingsControllerCallback =
- new MagnificationSettingsController.Callback() {
- @Override
- public void onSetMagnifierSize(int displayId, int index) {
- mHandler.post(() -> onSetMagnifierSizeInternal(displayId, index));
- mA11yLogger.logWithPosition(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED,
- index
- );
- }
-
- @Override
- public void onSetDiagonalScrolling(int displayId, boolean enable) {
- mHandler.post(() -> onSetDiagonalScrollingInternal(displayId, enable));
- }
-
- @Override
- public void onEditMagnifierSizeMode(int displayId, boolean enable) {
- mHandler.post(() -> onEditMagnifierSizeModeInternal(displayId, enable));
- mA11yLogger.log(enable
- ?
- MagnificationSettingsEvent
- .MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED
- : MagnificationSettingsEvent
- .MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED);
- }
-
- @Override
- public void onMagnifierScale(int displayId, float scale,
- boolean updatePersistence) {
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onPerformScaleAction(
- displayId, scale, updatePersistence);
- }
- mA11yLogger.logThrottled(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_ZOOM_SLIDER_CHANGED
- );
- }
-
- @Override
- public void onModeSwitch(int displayId, int newMode) {
- mHandler.post(() -> onModeSwitchInternal(displayId, newMode));
- }
-
- @Override
- public void onSettingsPanelVisibilityChanged(int displayId, boolean shown) {
- mHandler.post(() -> onSettingsPanelVisibilityChangedInternal(displayId, shown));
- }
- };
-
- @MainThread
- private void onSetMagnifierSizeInternal(int displayId, int index) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.changeMagnificationSize(index);
- }
- }
-
- @MainThread
- private void onSetDiagonalScrollingInternal(int displayId, boolean enable) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- windowMagnificationController.setDiagonalScrolling(enable);
- }
- }
-
- @MainThread
- private void onEditMagnifierSizeModeInternal(int displayId, boolean enable) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
- windowMagnificationController.setEditMagnifierSizeMode(enable);
- }
- }
-
- @MainThread
- private void onModeSwitchInternal(int displayId, int newMode) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- final boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
- final boolean isSwitchToWindowMode = (newMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- final boolean changed = isSwitchToWindowMode ^ isWindowMagnifierActivated;
- if (changed) {
- final MagnificationSettingsController magnificationSettingsController =
- mMagnificationSettingsSupplier.get(displayId);
- if (magnificationSettingsController != null) {
- magnificationSettingsController.closeMagnificationSettings();
- }
- if (mMagnificationConnectionImpl != null) {
- mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
- }
- }
- }
-
- @MainThread
- private void onSettingsPanelVisibilityChangedInternal(int displayId, boolean shown) {
- final WindowMagnificationController windowMagnificationController =
- mWindowMagnificationControllerSupplier.get(displayId);
- if (windowMagnificationController != null) {
- boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
- if (isWindowMagnifierActivated) {
- windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
- }
-
- if (shown) {
- mA11yLogger.logWithPosition(
- MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED,
- isWindowMagnifierActivated
- ? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
- : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
- );
- } else {
- mA11yLogger.log(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_CLOSED);
- }
- }
- }
-
- @Override
- public void requestMagnificationConnection(boolean connect) {
- if (connect) {
- setMagnificationConnection();
- } else {
- clearMagnificationConnection();
- }
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- pw.println(TAG);
- mWindowMagnificationControllerSupplier.forEach(
- magnificationController -> magnificationController.dump(pw));
- }
-
- private void setMagnificationConnection() {
- if (mMagnificationConnectionImpl == null) {
- mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
- mHandler);
- }
- mAccessibilityManager.setMagnificationConnection(
- mMagnificationConnectionImpl);
- }
-
- private void clearMagnificationConnection() {
- mAccessibilityManager.setMagnificationConnection(null);
- //TODO: destroy controllers.
- }
+ void moveWindowMagnifierToPosition(
+ int displayId,
+ float positionX,
+ float positionY,
+ IRemoteMagnificationAnimationCallback callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
index b5f3aef..c8b53ee 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationConnectionImpl.java
@@ -32,7 +32,7 @@
*
* @see IMagnificationConnection
*/
-class MagnificationConnectionImpl extends IMagnificationConnection.Stub {
+public class MagnificationConnectionImpl extends IMagnificationConnection.Stub {
private static final String TAG = "WindowMagnificationConnectionImpl";
@@ -40,7 +40,7 @@
private final Magnification mMagnification;
private final Handler mHandler;
- MagnificationConnectionImpl(@NonNull Magnification magnification,
+ public MagnificationConnectionImpl(@NonNull Magnification magnification,
@Main Handler mainHandler) {
mMagnification = magnification;
mHandler = mainHandler;
@@ -83,7 +83,7 @@
@Override
public void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
IRemoteMagnificationAnimationCallback callback) {
- mHandler.post(() -> mMagnification.moveWindowMagnifierToPositionInternal(
+ mHandler.post(() -> mMagnification.moveWindowMagnifierToPosition(
displayId, positionX, positionY, callback));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
new file mode 100644
index 0000000..6e5e44e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2019 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.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+
+import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.window.InputTransferToken;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.Flags;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+import javax.inject.Inject;
+
+/**
+ * Class to handle the interaction with
+ * {@link com.android.server.accessibility.AccessibilityManagerService}. It invokes
+ * {@link AccessibilityManager#setMagnificationConnection(IMagnificationConnection)}
+ * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
+ */
+@SysUISingleton
+public class MagnificationImpl implements Magnification, CommandQueue.Callbacks {
+ private static final String TAG = "Magnification";
+
+ @VisibleForTesting static final int DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS = 300;
+ private static final int MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL = 1;
+
+ private final ModeSwitchesController mModeSwitchesController;
+ private final Handler mHandler;
+ private final Executor mExecutor;
+ private final AccessibilityManager mAccessibilityManager;
+ private final CommandQueue mCommandQueue;
+ private final OverviewProxyService mOverviewProxyService;
+ private final DisplayTracker mDisplayTracker;
+ private final AccessibilityLogger mA11yLogger;
+
+ private MagnificationConnectionImpl mMagnificationConnectionImpl;
+ private SysUiState mSysUiState;
+
+ @VisibleForTesting
+ SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
+
+ private static class WindowMagnificationControllerSupplier extends
+ DisplayIdIndexSupplier<WindowMagnificationController> {
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final WindowMagnifierCallback mWindowMagnifierCallback;
+ private final SysUiState mSysUiState;
+ private final SecureSettings mSecureSettings;
+
+ WindowMagnificationControllerSupplier(Context context, Handler handler,
+ WindowMagnifierCallback windowMagnifierCallback,
+ DisplayManager displayManager, SysUiState sysUiState,
+ SecureSettings secureSettings) {
+ super(displayManager);
+ mContext = context;
+ mHandler = handler;
+ mWindowMagnifierCallback = windowMagnifierCallback;
+ mSysUiState = sysUiState;
+ mSecureSettings = secureSettings;
+ }
+
+ @Override
+ protected WindowMagnificationController createInstance(Display display) {
+ final Context windowContext = mContext.createWindowContext(display,
+ Flags.createWindowlessWindowMagnifier()
+ ? TYPE_ACCESSIBILITY_OVERLAY
+ : TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+ /* options */ null);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
+
+ Supplier<SurfaceControlViewHost> scvhSupplier = () ->
+ Flags.createWindowlessWindowMagnifier() ? new SurfaceControlViewHost(mContext,
+ mContext.getDisplay(), new InputTransferToken(), TAG) : null;
+
+ return new WindowMagnificationController(
+ windowContext,
+ mHandler,
+ new WindowMagnificationAnimationController(windowContext),
+ /* mirrorWindowControl= */ null,
+ new SurfaceControl.Transaction(),
+ mWindowMagnifierCallback,
+ mSysUiState,
+ mSecureSettings,
+ scvhSupplier,
+ new SfVsyncFrameCallbackProvider(),
+ WindowManagerGlobal::getWindowSession);
+ }
+ }
+
+ @VisibleForTesting
+ DisplayIdIndexSupplier<WindowMagnificationController> mWindowMagnificationControllerSupplier;
+
+ private static class FullscreenMagnificationControllerSupplier extends
+ DisplayIdIndexSupplier<FullscreenMagnificationController> {
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final Executor mExecutor;
+ private final IWindowManager mIWindowManager;
+
+ FullscreenMagnificationControllerSupplier(Context context,
+ DisplayManager displayManager,
+ Handler handler,
+ Executor executor, IWindowManager iWindowManager) {
+ super(displayManager);
+ mContext = context;
+ mHandler = handler;
+ mExecutor = executor;
+ mIWindowManager = iWindowManager;
+ }
+
+ @Override
+ protected FullscreenMagnificationController createInstance(Display display) {
+ final Context windowContext = mContext.createWindowContext(display,
+ TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
+ Supplier<SurfaceControlViewHost> scvhSupplier = () -> new SurfaceControlViewHost(
+ mContext, mContext.getDisplay(), new InputTransferToken(), TAG);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
+ return new FullscreenMagnificationController(
+ windowContext,
+ mHandler,
+ mExecutor,
+ windowContext.getSystemService(AccessibilityManager.class),
+ windowContext.getSystemService(WindowManager.class),
+ mIWindowManager,
+ scvhSupplier);
+ }
+ }
+
+ @VisibleForTesting
+ DisplayIdIndexSupplier<FullscreenMagnificationController>
+ mFullscreenMagnificationControllerSupplier;
+
+ private static class SettingsSupplier extends
+ DisplayIdIndexSupplier<MagnificationSettingsController> {
+
+ private final Context mContext;
+ private final MagnificationSettingsController.Callback mSettingsControllerCallback;
+ private final SecureSettings mSecureSettings;
+
+ SettingsSupplier(Context context,
+ MagnificationSettingsController.Callback settingsControllerCallback,
+ DisplayManager displayManager,
+ SecureSettings secureSettings) {
+ super(displayManager);
+ mContext = context;
+ mSettingsControllerCallback = settingsControllerCallback;
+ mSecureSettings = secureSettings;
+ }
+
+ @Override
+ protected MagnificationSettingsController createInstance(Display display) {
+ final Context windowContext = mContext.createWindowContext(display,
+ TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
+ return new MagnificationSettingsController(
+ windowContext,
+ new SfVsyncFrameCallbackProvider(),
+ mSettingsControllerCallback,
+ mSecureSettings);
+ }
+ }
+
+ @VisibleForTesting
+ DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
+
+ @Inject
+ public MagnificationImpl(Context context,
+ @Main Handler mainHandler, @Main Executor executor,
+ CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
+ SysUiState sysUiState, OverviewProxyService overviewProxyService,
+ SecureSettings secureSettings, DisplayTracker displayTracker,
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager, AccessibilityManager accessibilityManager) {
+ this(context, mainHandler.getLooper(), executor, commandQueue,
+ modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
+ displayTracker, displayManager, a11yLogger, iWindowManager, accessibilityManager);
+ }
+
+ @VisibleForTesting
+ public MagnificationImpl(Context context, Looper looper, @Main Executor executor,
+ CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
+ SysUiState sysUiState, OverviewProxyService overviewProxyService,
+ SecureSettings secureSettings, DisplayTracker displayTracker,
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager,
+ AccessibilityManager accessibilityManager) {
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ if (msg.what == MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL) {
+ showMagnificationButtonInternal(msg.arg1, msg.arg2);
+ }
+ }
+ };
+ mExecutor = executor;
+ mAccessibilityManager = accessibilityManager;
+ mCommandQueue = commandQueue;
+ mModeSwitchesController = modeSwitchesController;
+ mSysUiState = sysUiState;
+ mOverviewProxyService = overviewProxyService;
+ mDisplayTracker = displayTracker;
+ mA11yLogger = a11yLogger;
+ mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
+ mHandler, mWindowMagnifierCallback,
+ displayManager, sysUiState, secureSettings);
+ mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
+ context, displayManager, mHandler, mExecutor, iWindowManager);
+ mMagnificationSettingsSupplier = new SettingsSupplier(context,
+ mMagnificationSettingsControllerCallback, displayManager, secureSettings);
+
+ mModeSwitchesController.setClickListenerDelegate(
+ displayId -> mHandler.post(() -> {
+ toggleSettingsPanelVisibility(displayId);
+ }));
+ }
+
+ @Override
+ public void start() {
+ mCommandQueue.addCallback(this);
+ mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateSysUiStateFlag();
+ }
+ }
+ });
+ }
+
+ private void updateSysUiStateFlag() {
+ //TODO(b/187510533): support multi-display once SysuiState supports it.
+ final WindowMagnificationController controller =
+ mWindowMagnificationControllerSupplier.valueAt(
+ mDisplayTracker.getDefaultDisplayId());
+ if (controller != null) {
+ controller.updateSysUIStateFlag();
+ } else {
+ // The instance is initialized when there is an IPC request. Considering
+ // self-crash cases, we need to reset the flag in such situation.
+ mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
+ .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+ }
+ }
+
+ @Override
+ @MainThread
+ public void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+ float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
+ magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void setScaleForWindowMagnification(int displayId, float scale) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.setScale(scale);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ final WindowMagnificationController windowMagnificationcontroller =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationcontroller != null) {
+ windowMagnificationcontroller.moveWindowMagnifier(offsetX, offsetY);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void moveWindowMagnifierToPosition(int displayId, float positionX, float positionY,
+ IRemoteMagnificationAnimationCallback callback) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
+ callback);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void disableWindowMagnification(int displayId,
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.deleteWindowMagnification(callback);
+ }
+ }
+
+ @Override
+ @MainThread
+ public void onFullscreenMagnificationActivationChanged(int displayId, boolean activated) {
+ final FullscreenMagnificationController fullscreenMagnificationController =
+ mFullscreenMagnificationControllerSupplier.get(displayId);
+ if (fullscreenMagnificationController != null) {
+ fullscreenMagnificationController.onFullscreenMagnificationActivationChanged(activated);
+ }
+ }
+
+ @MainThread
+ void toggleSettingsPanelVisibility(int displayId) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ magnificationSettingsController.toggleSettingsPanelVisibility();
+ }
+ }
+
+ @Override
+ @MainThread
+ public void hideMagnificationSettingsPanel(int displayId) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ magnificationSettingsController.closeMagnificationSettings();
+ }
+ }
+
+ boolean isMagnificationSettingsPanelShowing(int displayId) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ return magnificationSettingsController.isMagnificationSettingsShowing();
+ }
+ return false;
+ }
+
+ @Override
+ @MainThread
+ public void showMagnificationButton(int displayId, int magnificationMode) {
+ if (Flags.delayShowMagnificationButton()) {
+ if (mHandler.hasMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL)) {
+ return;
+ }
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
+ DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS);
+ } else {
+ showMagnificationButtonInternal(displayId, magnificationMode);
+ }
+ }
+
+ @MainThread
+ private void showMagnificationButtonInternal(int displayId, int magnificationMode) {
+ // not to show mode switch button if settings panel is already showing to
+ // prevent settings panel be covered by the button.
+ if (isMagnificationSettingsPanelShowing(displayId)) {
+ return;
+ }
+ mModeSwitchesController.showButton(displayId, magnificationMode);
+ }
+
+ @Override
+ @MainThread
+ public void removeMagnificationButton(int displayId) {
+ if (Flags.delayShowMagnificationButton()) {
+ mHandler.removeMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL);
+ }
+ mModeSwitchesController.removeButton(displayId);
+ }
+
+ @Override
+ @MainThread
+ public void setUserMagnificationScale(int userId, int displayId, float scale) {
+ SparseArray<Float> scales = mUsersScales.get(userId);
+ if (scales == null) {
+ scales = new SparseArray<>();
+ mUsersScales.put(userId, scales);
+ }
+ if (scales.contains(displayId) && scales.get(displayId) == scale) {
+ return;
+ }
+ scales.put(displayId, scale);
+
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ magnificationSettingsController.setMagnificationScale(scale);
+ }
+
+ @VisibleForTesting
+ final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
+ @Override
+ public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
+ }
+ }
+
+ @Override
+ public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
+ }
+ }
+
+ @Override
+ public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
+ displayId, scale, updatePersistence);
+ }
+ }
+
+ @Override
+ public void onAccessibilityActionPerformed(int displayId) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+ }
+ }
+
+ @Override
+ public void onMove(int displayId) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onMove(displayId);
+ }
+ }
+
+ @Override
+ public void onClickSettingsButton(int displayId) {
+ mHandler.post(() -> {
+ toggleSettingsPanelVisibility(displayId);
+ });
+ }
+ };
+
+ @VisibleForTesting
+ final MagnificationSettingsController.Callback mMagnificationSettingsControllerCallback =
+ new MagnificationSettingsController.Callback() {
+ @Override
+ public void onSetMagnifierSize(int displayId, int index) {
+ mHandler.post(() -> onSetMagnifierSizeInternal(displayId, index));
+ mA11yLogger.logWithPosition(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED,
+ index
+ );
+ }
+
+ @Override
+ public void onSetDiagonalScrolling(int displayId, boolean enable) {
+ mHandler.post(() -> onSetDiagonalScrollingInternal(displayId, enable));
+ }
+
+ @Override
+ public void onEditMagnifierSizeMode(int displayId, boolean enable) {
+ mHandler.post(() -> onEditMagnifierSizeModeInternal(displayId, enable));
+ mA11yLogger.log(enable
+ ?
+ MagnificationSettingsEvent
+ .MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED
+ : MagnificationSettingsEvent
+ .MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED);
+ }
+
+ @Override
+ public void onMagnifierScale(int displayId, float scale,
+ boolean updatePersistence) {
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onPerformScaleAction(
+ displayId, scale, updatePersistence);
+ }
+ mA11yLogger.logThrottled(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_ZOOM_SLIDER_CHANGED
+ );
+ }
+
+ @Override
+ public void onModeSwitch(int displayId, int newMode) {
+ mHandler.post(() -> onModeSwitchInternal(displayId, newMode));
+ }
+
+ @Override
+ public void onSettingsPanelVisibilityChanged(int displayId, boolean shown) {
+ mHandler.post(() -> onSettingsPanelVisibilityChangedInternal(displayId, shown));
+ }
+ };
+
+ @MainThread
+ private void onSetMagnifierSizeInternal(int displayId, int index) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.changeMagnificationSize(index);
+ }
+ }
+
+ @MainThread
+ private void onSetDiagonalScrollingInternal(int displayId, boolean enable) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ windowMagnificationController.setDiagonalScrolling(enable);
+ }
+ }
+
+ @MainThread
+ private void onEditMagnifierSizeModeInternal(int displayId, boolean enable) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
+ windowMagnificationController.setEditMagnifierSizeMode(enable);
+ }
+ }
+
+ @MainThread
+ private void onModeSwitchInternal(int displayId, int newMode) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ final boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
+ final boolean isSwitchToWindowMode = (newMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ final boolean changed = isSwitchToWindowMode ^ isWindowMagnifierActivated;
+ if (changed) {
+ final MagnificationSettingsController magnificationSettingsController =
+ mMagnificationSettingsSupplier.get(displayId);
+ if (magnificationSettingsController != null) {
+ magnificationSettingsController.closeMagnificationSettings();
+ }
+ if (mMagnificationConnectionImpl != null) {
+ mMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
+ }
+ }
+ }
+
+ @MainThread
+ private void onSettingsPanelVisibilityChangedInternal(int displayId, boolean shown) {
+ final WindowMagnificationController windowMagnificationController =
+ mWindowMagnificationControllerSupplier.get(displayId);
+ if (windowMagnificationController != null) {
+ boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
+ if (isWindowMagnifierActivated) {
+ windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
+ }
+
+ if (shown) {
+ mA11yLogger.logWithPosition(
+ MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED,
+ isWindowMagnifierActivated
+ ? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
+ : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
+ );
+ } else {
+ mA11yLogger.log(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_CLOSED);
+ }
+ }
+ }
+
+ @Override
+ public void requestMagnificationConnection(boolean connect) {
+ if (connect) {
+ setMagnificationConnection();
+ } else {
+ clearMagnificationConnection();
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
+ pw.println(TAG);
+ mWindowMagnificationControllerSupplier.forEach(
+ magnificationController -> magnificationController.dump(pw));
+ }
+
+ private void setMagnificationConnection() {
+ if (mMagnificationConnectionImpl == null) {
+ mMagnificationConnectionImpl = new MagnificationConnectionImpl(this,
+ mHandler);
+ }
+ mAccessibilityManager.setMagnificationConnection(
+ mMagnificationConnectionImpl);
+ }
+
+ private void clearMagnificationConnection() {
+ mAccessibilityManager.setMagnificationConnection(null);
+ //TODO: destroy controllers.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 68a69d3..37e9dc1a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -42,6 +42,7 @@
import android.view.KeyEvent;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.Flags;
import com.android.internal.R;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
@@ -178,6 +179,18 @@
private static final int SYSTEM_ACTION_ID_DPAD_CENTER =
AccessibilityService.GLOBAL_ACTION_DPAD_CENTER; // 20
+ /**
+ * Action ID to trigger menu key event.
+ */
+ private static final int SYSTEM_ACTION_ID_MENU =
+ AccessibilityService.GLOBAL_ACTION_MENU; // 21
+
+ /**
+ * Action ID to trigger media play/pause key event.
+ */
+ private static final int SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE =
+ AccessibilityService.GLOBAL_ACTION_MEDIA_PLAY_PAUSE; // 22
+
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
private final SystemActionsBroadcastReceiver mReceiver;
@@ -307,6 +320,14 @@
R.string.accessibility_system_action_dpad_center_label,
SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER);
+ RemoteAction actionMenu = createRemoteAction(
+ R.string.accessibility_system_action_menu_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_MENU);
+
+ RemoteAction actionMediaPlayPause = createRemoteAction(
+ R.string.accessibility_system_action_media_play_pause_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_MEDIA_PLAY_PAUSE);
+
mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
@@ -326,6 +347,8 @@
mA11yManager.registerSystemAction(actionDpadLeft, SYSTEM_ACTION_ID_DPAD_LEFT);
mA11yManager.registerSystemAction(actionDpadRight, SYSTEM_ACTION_ID_DPAD_RIGHT);
mA11yManager.registerSystemAction(actionDpadCenter, SYSTEM_ACTION_ID_DPAD_CENTER);
+ mA11yManager.registerSystemAction(actionMenu, SYSTEM_ACTION_ID_MENU);
+ mA11yManager.registerSystemAction(actionMediaPlayPause, SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE);
registerOrUnregisterDismissNotificationShadeAction();
}
@@ -435,6 +458,14 @@
labelId = R.string.accessibility_system_action_dpad_center_label;
intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER;
break;
+ case SYSTEM_ACTION_ID_MENU:
+ labelId = R.string.accessibility_system_action_menu_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_MENU;
+ break;
+ case SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE:
+ labelId = R.string.accessibility_system_action_media_play_pause_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_MEDIA_PLAY_PAUSE;
+ break;
default:
return;
}
@@ -570,6 +601,16 @@
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER);
}
+ @VisibleForTesting
+ void handleMenu() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MENU);
+ }
+
+ @VisibleForTesting
+ void handleMediaPlayPause() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ }
+
private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -593,6 +634,9 @@
private static final String INTENT_ACTION_DPAD_LEFT = "SYSTEM_ACTION_DPAD_LEFT";
private static final String INTENT_ACTION_DPAD_RIGHT = "SYSTEM_ACTION_DPAD_RIGHT";
private static final String INTENT_ACTION_DPAD_CENTER = "SYSTEM_ACTION_DPAD_CENTER";
+ private static final String INTENT_ACTION_MENU = "SYSTEM_ACTION_MENU";
+ private static final String INTENT_ACTION_MEDIA_PLAY_PAUSE =
+ "SYSTEM_ACTION_MEDIA_PLAY_PAUSE";
private PendingIntent createPendingIntent(Context context, String intentAction) {
switch (intentAction) {
@@ -613,7 +657,9 @@
case INTENT_ACTION_DPAD_DOWN:
case INTENT_ACTION_DPAD_LEFT:
case INTENT_ACTION_DPAD_RIGHT:
- case INTENT_ACTION_DPAD_CENTER: {
+ case INTENT_ACTION_DPAD_CENTER:
+ case INTENT_ACTION_MENU:
+ case INTENT_ACTION_MEDIA_PLAY_PAUSE: {
Intent intent = new Intent(intentAction);
intent.setPackage(context.getPackageName());
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -646,6 +692,8 @@
intentFilter.addAction(INTENT_ACTION_DPAD_LEFT);
intentFilter.addAction(INTENT_ACTION_DPAD_RIGHT);
intentFilter.addAction(INTENT_ACTION_DPAD_CENTER);
+ intentFilter.addAction(INTENT_ACTION_MENU);
+ intentFilter.addAction(INTENT_ACTION_MEDIA_PLAY_PAUSE);
return intentFilter;
}
@@ -725,6 +773,18 @@
handleDpadCenter();
break;
}
+ case INTENT_ACTION_MENU: {
+ if (Flags.globalActionMenu()) {
+ handleMenu();
+ }
+ break;
+ }
+ case INTENT_ACTION_MEDIA_PLAY_PAUSE: {
+ if (Flags.globalActionMediaPlayPause()) {
+ handleMediaPlayPause();
+ }
+ break;
+ }
default:
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 5458ab1..b37ba89 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1631,8 +1631,23 @@
boolean bRTL = isRTL(mContext);
final int initSize = Math.min(mWindowBounds.width(), mWindowBounds.height()) / 3;
- final int maxHeightSize = mWindowBounds.height() - 2 * mMirrorSurfaceMargin;
- final int maxWidthSize = mWindowBounds.width() - 2 * mMirrorSurfaceMargin;
+ int maxHeightSize;
+ int maxWidthSize;
+ if (Flags.redesignMagnificationWindowSize()) {
+ // mOuterBorderSize = transparent margin area
+ // mMirrorSurfaceMargin = transparent margin area + orange border width
+ // We would like to allow the width and height to be full size. Therefore, the max
+ // frame size could be calculated as (window bounds - 2 * orange border width).
+ maxHeightSize =
+ mWindowBounds.height() - 2 * (mMirrorSurfaceMargin - mOuterBorderSize);
+ maxWidthSize =
+ mWindowBounds.width() - 2 * (mMirrorSurfaceMargin - mOuterBorderSize);
+ } else {
+ maxHeightSize =
+ mWindowBounds.height() - 2 * mMirrorSurfaceMargin;
+ maxWidthSize =
+ mWindowBounds.width() - 2 * mMirrorSurfaceMargin;
+ }
Rect tempRect = new Rect();
tempRect.set(mMagnificationFrame);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index dafd5f8..030d147 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -72,6 +72,7 @@
private final Handler mHandler;
private boolean mIsFadeEffectEnabled;
private Runnable mSpringAnimationsEndAction;
+ private PointF mAnimationEndPosition = new PointF();
// Cache the animations state of {@link DynamicAnimation.TRANSLATION_X} and {@link
// DynamicAnimation.TRANSLATION_Y} to be well controlled by the touch handler
@@ -104,10 +105,12 @@
@Override
public void onRadiiAnimationStop() {}
});
+ mAnimationEndPosition = mMenuView.getMenuPosition();
}
void moveToPosition(PointF position) {
moveToPosition(position, /* animateMovement = */ false);
+ mAnimationEndPosition = position;
}
/* Moves position without updating underlying percentage position. Can be animated. */
@@ -129,6 +132,7 @@
} else {
DynamicAnimation.TRANSLATION_X.setValue(mMenuView, positionX);
}
+ mAnimationEndPosition.x = positionX;
}
void moveToPositionY(float positionY) {
@@ -144,6 +148,7 @@
} else {
DynamicAnimation.TRANSLATION_Y.setValue(mMenuView, positionY);
}
+ mAnimationEndPosition.y = positionY;
}
void moveToPositionYIfNeeded(float positionY) {
@@ -259,6 +264,9 @@
cancelAnimation(property);
mPositionAnimations.put(property, flingAnimation);
+ if (finalPosition != null) {
+ setAnimationEndPosition(property, finalPosition);
+ }
flingAnimation.start();
}
@@ -292,6 +300,7 @@
cancelAnimation(property);
mPositionAnimations.put(property, springAnimation);
+ setAnimationEndPosition(property, finalPosition);
springAnimation.animateToFinalPosition(finalPosition);
}
@@ -385,6 +394,21 @@
mPositionAnimations.get(property).cancel();
}
+ private void setAnimationEndPosition(
+ DynamicAnimation.ViewProperty property, Float endPosition) {
+ if (property.equals(DynamicAnimation.TRANSLATION_X)) {
+ mAnimationEndPosition.x = endPosition;
+ }
+ if (property.equals(DynamicAnimation.TRANSLATION_Y)) {
+ mAnimationEndPosition.y = endPosition;
+ }
+ }
+
+ void skipAnimations() {
+ cancelAnimations();
+ moveToPosition(mAnimationEndPosition, false);
+ }
+
@VisibleForTesting
DynamicAnimation getAnimation(DynamicAnimation.ViewProperty property) {
return mPositionAnimations.getOrDefault(property, null);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 0c67c50..9d573d3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -205,8 +205,9 @@
return;
}
- setAccessibilityServiceState(getContext(), serviceComponentName, /* enabled= */
- false);
+ setAccessibilityServiceState(
+ getContext(), serviceComponentName, /* enabled= */ false,
+ mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT));
});
}
@@ -334,6 +335,7 @@
mDragToInteractView.updateResources();
mDismissView.updateResources();
mDragToInteractAnimationController.updateResources();
+ mMenuAnimationController.skipAnimations();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
index 02fa003..f81124e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -220,7 +220,8 @@
if (mActiveHearingDevice == null) {
return emptyList();
}
- return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice());
+ return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
+ BluetoothHapPresetInfo::isAvailable).toList();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 61b4401..4035e95 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -323,12 +323,15 @@
// When we stop monitoring touches, we must ensure that all active touch sessions and
// descendants informed of the removal so any cleanup for active tracking can proceed.
- mMainExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
- while (touchSession != null) {
- touchSession.onRemoved();
- touchSession = touchSession.getPredecessor();
- }
- }));
+ mMainExecutor.execute(() -> {
+ mActiveTouchSessions.forEach(touchSession -> {
+ while (touchSession != null) {
+ touchSession.onRemoved();
+ touchSession = touchSession.getPredecessor();
+ }
+ });
+ mActiveTouchSessions.clear();
+ });
mCurrentInputSession.dispose();
mCurrentInputSession = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 01cc33c..e03d160 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -45,7 +45,6 @@
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags.udfpsViewPerformance
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -82,67 +81,66 @@
private const val TAG = "UdfpsControllerOverlay"
-@VisibleForTesting
-const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
+@VisibleForTesting const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
/**
* Keeps track of the overlay state and UI resources associated with a single FingerprintService
- * request. This state can persist across configuration changes via the [show] and [hide]
- * methods.
+ * request. This state can persist across configuration changes via the [show] and [hide] methods.
*/
@ExperimentalCoroutinesApi
@UiThread
-class UdfpsControllerOverlay @JvmOverloads constructor(
- private val context: Context,
- private val inflater: LayoutInflater,
- private val windowManager: WindowManager,
- private val accessibilityManager: AccessibilityManager,
- private val statusBarStateController: StatusBarStateController,
- private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val dialogManager: SystemUIDialogManager,
- private val dumpManager: DumpManager,
- private val transitionController: LockscreenShadeTransitionController,
- private val configurationController: ConfigurationController,
- private val keyguardStateController: KeyguardStateController,
- private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
- val requestId: Long,
- @RequestReason val requestReason: Int,
- private val controllerCallback: IUdfpsOverlayControllerCallback,
- private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
- private val activityTransitionAnimator: ActivityTransitionAnimator,
- private val primaryBouncerInteractor: PrimaryBouncerInteractor,
- private val alternateBouncerInteractor: AlternateBouncerInteractor,
- private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
- private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
- private val transitionInteractor: KeyguardTransitionInteractor,
- private val selectedUserInteractor: SelectedUserInteractor,
- private val deviceEntryUdfpsTouchOverlayViewModel:
- Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
- private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
- private val shadeInteractor: ShadeInteractor,
- private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
- private val powerInteractor: PowerInteractor,
- @Application private val scope: CoroutineScope,
+class UdfpsControllerOverlay
+@JvmOverloads
+constructor(
+ private val context: Context,
+ private val inflater: LayoutInflater,
+ private val windowManager: WindowManager,
+ private val accessibilityManager: AccessibilityManager,
+ private val statusBarStateController: StatusBarStateController,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val dialogManager: SystemUIDialogManager,
+ private val dumpManager: DumpManager,
+ private val transitionController: LockscreenShadeTransitionController,
+ private val configurationController: ConfigurationController,
+ private val keyguardStateController: KeyguardStateController,
+ private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
+ private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
+ val requestId: Long,
+ @RequestReason val requestReason: Int,
+ private val controllerCallback: IUdfpsOverlayControllerCallback,
+ private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+ private val activityTransitionAnimator: ActivityTransitionAnimator,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
+ private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val transitionInteractor: KeyguardTransitionInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val deviceEntryUdfpsTouchOverlayViewModel: Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
+ private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
+ private val shadeInteractor: ShadeInteractor,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ private val powerInteractor: PowerInteractor,
+ @Application private val scope: CoroutineScope,
) {
private val currentStateUpdatedToOffAodOrDozing: Flow<Unit> =
transitionInteractor.currentKeyguardState
.filter {
- it == KeyguardState.OFF ||
- it == KeyguardState.AOD ||
- it == KeyguardState.DOZING
+ it == KeyguardState.OFF || it == KeyguardState.AOD || it == KeyguardState.DOZING
}
- .map { } // map to Unit
+ .map {} // map to Unit
private var listenForCurrentKeyguardState: Job? = null
private var addViewRunnable: Runnable? = null
private var overlayViewLegacy: UdfpsView? = null
private set
+
private var overlayTouchView: UdfpsTouchOverlay? = null
/**
- * Get the current UDFPS overlay touch view which is a different View depending on whether
- * the DeviceEntryUdfpsRefactor flag is enabled or not.
+ * Get the current UDFPS overlay touch view which is a different View depending on whether the
+ * DeviceEntryUdfpsRefactor flag is enabled or not.
+ *
* @return The view, when [isShowing], else null
*/
fun getTouchOverlay(): View? {
@@ -158,23 +156,28 @@
private var overlayTouchListener: TouchExplorationStateChangeListener? = null
- private val coreLayoutParams = WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- 0 /* flags set in computeLayoutParams() */,
- PixelFormat.TRANSLUCENT
- ).apply {
- title = TAG
- fitInsetsTypes = 0
- gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
- layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
- WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
- privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
- WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION
- // Avoid announcing window title.
- accessibilityTitle = " "
- inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
- }
+ private val coreLayoutParams =
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ 0 /* flags set in computeLayoutParams() */,
+ PixelFormat.TRANSLUCENT
+ )
+ .apply {
+ title = TAG
+ fitInsetsTypes = 0
+ gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
+ layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ flags =
+ (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+ privateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
+ WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION
+ // Avoid announcing window title.
+ accessibilityTitle = " "
+ inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
+ }
/** If the overlay is currently showing. */
val isShowing: Boolean
@@ -209,51 +212,51 @@
sensorBounds = Rect(params.sensorBounds)
try {
if (DeviceEntryUdfpsRefactor.isEnabled) {
- overlayTouchView = (inflater.inflate(
- R.layout.udfps_touch_overlay, null, false
- ) as UdfpsTouchOverlay).apply {
- // This view overlaps the sensor area
- // prevent it from being selectable during a11y
- if (requestReason.isImportantForAccessibility()) {
- importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
- }
+ overlayTouchView =
+ (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
+ as UdfpsTouchOverlay)
+ .apply {
+ // This view overlaps the sensor area
+ // prevent it from being selectable during a11y
+ if (requestReason.isImportantForAccessibility()) {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ }
- addViewNowOrLater(this, null)
- when (requestReason) {
- REASON_AUTH_KEYGUARD ->
- UdfpsTouchOverlayBinder.bind(
- view = this,
- viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
- udfpsOverlayInteractor = udfpsOverlayInteractor,
- )
- else ->
- UdfpsTouchOverlayBinder.bind(
- view = this,
- viewModel = defaultUdfpsTouchOverlayViewModel.get(),
- udfpsOverlayInteractor = udfpsOverlayInteractor,
- )
- }
- }
+ addViewNowOrLater(this, null)
+ when (requestReason) {
+ REASON_AUTH_KEYGUARD ->
+ UdfpsTouchOverlayBinder.bind(
+ view = this,
+ viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ )
+ else ->
+ UdfpsTouchOverlayBinder.bind(
+ view = this,
+ viewModel = defaultUdfpsTouchOverlayViewModel.get(),
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ )
+ }
+ }
} else {
- overlayViewLegacy = (inflater.inflate(
- R.layout.udfps_view, null, false
- ) as UdfpsView).apply {
- overlayParams = params
- setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
- val animation = inflateUdfpsAnimation(this, controller)
- if (animation != null) {
- animation.init()
- animationViewController = animation
- }
- // This view overlaps the sensor area
- // prevent it from being selectable during a11y
- if (requestReason.isImportantForAccessibility()) {
- importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
- }
+ overlayViewLegacy =
+ (inflater.inflate(R.layout.udfps_view, null, false) as UdfpsView).apply {
+ overlayParams = params
+ setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
+ val animation = inflateUdfpsAnimation(this, controller)
+ if (animation != null) {
+ animation.init()
+ animationViewController = animation
+ }
+ // This view overlaps the sensor area
+ // prevent it from being selectable during a11y
+ if (requestReason.isImportantForAccessibility()) {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ }
- addViewNowOrLater(this, animation)
- sensorRect = sensorBounds
- }
+ addViewNowOrLater(this, animation)
+ sensorRect = sensorBounds
+ }
}
getTouchOverlay()?.apply {
touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
@@ -269,7 +272,7 @@
}
}
accessibilityManager.addTouchExplorationStateChangeListener(
- overlayTouchListener!!
+ overlayTouchListener!!
)
overlayTouchListener?.onTouchExplorationStateChanged(true)
}
@@ -284,30 +287,18 @@
}
private fun addViewNowOrLater(view: View, animation: UdfpsAnimationViewController<*>?) {
- if (udfpsViewPerformance()) {
- addViewRunnable = kotlinx.coroutines.Runnable {
+ addViewRunnable =
+ kotlinx.coroutines.Runnable {
Trace.setCounter("UdfpsAddView", 1)
- windowManager.addView(
- view,
- coreLayoutParams.updateDimensions(animation)
- )
+ windowManager.addView(view, coreLayoutParams.updateDimensions(animation))
}
- if (powerInteractor.detailedWakefulness.value.isAwake()) {
- // Device is awake, so we add the view immediately.
- addViewIfPending()
- } else {
- listenForCurrentKeyguardState?.cancel()
- listenForCurrentKeyguardState = scope.launch {
- currentStateUpdatedToOffAodOrDozing.collect {
- addViewIfPending()
- }
- }
- }
+ if (powerInteractor.detailedWakefulness.value.isAwake()) {
+ // Device is awake, so we add the view immediately.
+ addViewIfPending()
} else {
- windowManager.addView(
- view,
- coreLayoutParams.updateDimensions(animation)
- )
+ listenForCurrentKeyguardState?.cancel()
+ listenForCurrentKeyguardState =
+ scope.launch { currentStateUpdatedToOffAodOrDozing.collect { addViewIfPending() } }
}
}
@@ -340,23 +331,26 @@
): UdfpsAnimationViewController<*>? {
DeviceEntryUdfpsRefactor.assertInLegacyMode()
- val isEnrollment = when (requestReason) {
- REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true
- else -> false
- }
+ val isEnrollment =
+ when (requestReason) {
+ REASON_ENROLL_FIND_SENSOR,
+ REASON_ENROLL_ENROLLING -> true
+ else -> false
+ }
- val filteredRequestReason = if (isEnrollment && shouldRemoveEnrollmentUi()) {
- REASON_AUTH_OTHER
- } else {
- requestReason
- }
+ val filteredRequestReason =
+ if (isEnrollment && shouldRemoveEnrollmentUi()) {
+ REASON_AUTH_OTHER
+ } else {
+ requestReason
+ }
return when (filteredRequestReason) {
REASON_ENROLL_FIND_SENSOR,
REASON_ENROLL_ENROLLING -> {
// Enroll udfps UI is handled by settings, so use empty view here
UdfpsFpmEmptyViewController(
- view.addUdfpsView(R.layout.udfps_fpm_empty_view){
+ view.addUdfpsView(R.layout.udfps_fpm_empty_view) {
updateAccessibilityViewLocation(sensorBounds)
},
statusBarStateController,
@@ -434,14 +428,10 @@
udfpsDisplayModeProvider.disable(null)
}
getTouchOverlay()?.apply {
- if (udfpsViewPerformance()) {
- if (this.parent != null) {
- windowManager.removeView(this)
- }
- Trace.setCounter("UdfpsAddView", 0)
- } else {
+ if (this.parent != null) {
windowManager.removeView(this)
}
+ Trace.setCounter("UdfpsAddView", 0)
setOnTouchListener(null)
setOnHoverListener(null)
overlayTouchListener?.let {
@@ -475,22 +465,19 @@
val paddingX = animation?.paddingX ?: 0
val paddingY = animation?.paddingY ?: 0
- val isEnrollment = when (requestReason) {
- REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true
- else -> false
- }
+ val isEnrollment =
+ when (requestReason) {
+ REASON_ENROLL_FIND_SENSOR,
+ REASON_ENROLL_ENROLLING -> true
+ else -> false
+ }
// Use expanded overlay unless touchExploration enabled
var rotatedBounds =
if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) {
Rect(overlayParams.sensorBounds)
} else {
- Rect(
- 0,
- 0,
- overlayParams.naturalDisplayWidth,
- overlayParams.naturalDisplayHeight
- )
+ Rect(0, 0, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight)
}
val rot = overlayParams.rotation
@@ -498,7 +485,8 @@
if (!shouldRotate(animation)) {
Log.v(
TAG,
- "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
+ "Skip rotating UDFPS bounds " +
+ Surface.rotationToString(rot) +
" animation=$animation" +
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
" isOccluded=${keyguardStateController.isOccluded}"
@@ -559,6 +547,4 @@
@RequestReason
private fun Int.isImportantForAccessibility() =
- this == REASON_ENROLL_FIND_SENSOR ||
- this == REASON_ENROLL_ENROLLING ||
- this == REASON_AUTH_BP
+ this == REASON_ENROLL_FIND_SENSOR || this == REASON_ENROLL_ENROLLING || this == REASON_AUTH_BP
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index c08756f..5e2b5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -74,7 +74,7 @@
val isConfirmationRequired: Flow<Boolean>
/** Fingerprint sensor type */
- val sensorType: Flow<FingerprintSensorType>
+ val fingerprintSensorType: Flow<FingerprintSensorType>
/** Switch to the credential view. */
fun onSwitchToCredential()
@@ -154,7 +154,8 @@
}
}
- override val sensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType
+ override val fingerprintSensorType: Flow<FingerprintSensorType> =
+ fingerprintPropertyRepository.sensorType
override fun onSwitchToCredential() {
val modalities: BiometricModalities =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 408e2c2..c868d01 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -580,18 +580,23 @@
}
}
- private suspend fun getHelpForSuccessfulAuthentication(
+ private fun getHelpForSuccessfulAuthentication(
authenticatedModality: BiometricModality,
- ): Int? =
- when {
- // for coex, show a message when face succeeds after fingerprint has also started
- modalities.hasFaceAndFingerprint &&
- (viewModel.fingerprintStartMode.first() != FingerprintStartMode.Pending) &&
- (authenticatedModality == BiometricModality.Face) ->
- R.string.biometric_dialog_tap_confirm_with_face_alt_1
- else -> null
+ ): Int? {
+ // for coex, show a message when face succeeds after fingerprint has also started
+ if (authenticatedModality != BiometricModality.Face) {
+ return null
}
+ if (modalities.hasUdfps) {
+ return R.string.biometric_dialog_tap_confirm_with_face_alt_1
+ }
+ if (modalities.hasSfps) {
+ return R.string.biometric_dialog_tap_confirm_with_face_sfps
+ }
+ return null
+ }
+
fun onAuthenticationFailed(
@BiometricAuthenticator.Modality modality: Int,
failureReason: String,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 900d7cc..5e0e2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -17,15 +17,11 @@
package com.android.systemui.biometrics.ui.binder
-import android.graphics.drawable.AnimatedVectorDrawable
import android.util.Log
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieOnCompositionLoadedListener
-import com.airbnb.lottie.LottieListener
import com.android.settingslib.widget.LottieColorUtils
import com.android.systemui.Flags.constraintBp
import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
@@ -66,85 +62,35 @@
iconOverlayView.layoutParams.height = iconViewLayoutParamSizeOverride.second
}
- var faceIcon: AnimatedVectorDrawable? = null
-
- fun updateXmlIconAsset(
- iconAsset: Int,
- shouldAnimateIconView: Boolean,
- activeAuthType: AuthType
- ) {
- faceIcon?.stop()
- faceIcon = iconView.context.getDrawable(iconAsset) as AnimatedVectorDrawable
- faceIcon?.apply {
- iconView.setIconFailureListener(iconAsset, activeAuthType)
- iconView.setImageDrawable(this)
- if (shouldAnimateIconView) {
- forceAnimationOnUI()
- start()
- }
- }
- }
-
- fun updateJsonIconAsset(
- iconAsset: Int,
- shouldAnimateIconView: Boolean,
- activeAuthType: AuthType
- ) {
- iconView.setIconFailureListener(iconAsset, activeAuthType)
- iconView.setAnimation(iconAsset)
- iconView.frame = 0
-
- if (shouldAnimateIconView) {
- iconView.playAnimation()
- }
- }
-
if (!constraintBp()) {
launch {
var lottieOnCompositionLoadedListener: LottieOnCompositionLoadedListener? =
null
- combine(viewModel.activeAuthType, viewModel.iconSize, ::Pair).collect {
- (activeAuthType, iconSize) ->
- // Every time after bp shows, [isIconViewLoaded] is set to false in
- // [BiometricViewSizeBinder]. Then when biometric prompt view is redrew
- // (when size or activeAuthType changes), we need to update
- // [isIconViewLoaded] here to keep it correct.
- when (activeAuthType) {
- AuthType.Fingerprint,
- AuthType.Coex -> {
- /**
- * View is only set visible in BiometricViewSizeBinder once
- * PromptSize is determined that accounts for iconView size, to
- * prevent prompt resizing being visible to the user.
- *
- * TODO(b/288175072): May be able to remove this once constraint
- * layout is implemented
- */
- if (lottieOnCompositionLoadedListener != null) {
- iconView.removeLottieOnCompositionLoadedListener(
- lottieOnCompositionLoadedListener!!
- )
- }
- lottieOnCompositionLoadedListener =
- LottieOnCompositionLoadedListener {
- promptViewModel.setIsIconViewLoaded(true)
- }
- iconView.addLottieOnCompositionLoadedListener(
- lottieOnCompositionLoadedListener!!
- )
- }
- AuthType.Face -> {
- /**
- * Set to true by default since face icon is a drawable, which
- * doesn't have a LottieOnCompositionLoadedListener equivalent.
- *
- * TODO(b/318569643): To be updated once face assets are updated
- * from drawables
- */
- promptViewModel.setIsIconViewLoaded(true)
- }
+ viewModel.iconSize.collect { iconSize ->
+ /**
+ * When we bind the BiometricPrompt View and ViewModel in
+ * [BiometricViewBinder], the view is set invisible and
+ * [isIconViewLoaded] is set to false. We configure the iconView with a
+ * LottieOnCompositionLoadedListener that sets [isIconViewLoaded] to
+ * true, in order to wait for the iconView to load before determining
+ * the prompt size, and prevent any prompt resizing from being visible
+ * to the user.
+ *
+ * TODO(b/288175072): May be able to remove this once constraint layout
+ * is unflagged
+ */
+ if (lottieOnCompositionLoadedListener != null) {
+ iconView.removeLottieOnCompositionLoadedListener(
+ lottieOnCompositionLoadedListener!!
+ )
}
+ lottieOnCompositionLoadedListener = LottieOnCompositionLoadedListener {
+ promptViewModel.setIsIconViewLoaded(true)
+ }
+ iconView.addLottieOnCompositionLoadedListener(
+ lottieOnCompositionLoadedListener!!
+ )
if (iconViewLayoutParamSizeOverride == null) {
iconView.layoutParams.width = iconSize.first
@@ -171,51 +117,12 @@
.collect { (iconAsset, activeAuthType, shouldAnimateIconView, showingError)
->
if (iconAsset != -1) {
- when (activeAuthType) {
- AuthType.Fingerprint,
- AuthType.Coex -> {
- // TODO(b/318569643): Until assets unified to one type, this
- // check
- // is needed in face-auth-error-triggered implicit ->
- // explicit
- // coex auth transition, in case iconAsset updates to
- // face_dialog_dark_to_error (XML) after activeAuthType
- // updates
- // from AuthType.Face (which expects XML)
- // to AuthType.Coex (which expects JSON)
- if (iconAsset == R.drawable.face_dialog_dark_to_error) {
- updateXmlIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- } else {
- updateJsonIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- }
- }
- AuthType.Face -> {
- // TODO(b/318569643): Consolidate logic once all face auth
- // assets are migrated from drawable to json
- if (iconAsset == R.raw.face_dialog_authenticating) {
- updateJsonIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- } else {
- updateXmlIconAsset(
- iconAsset,
- shouldAnimateIconView,
- activeAuthType
- )
- }
- }
- }
- LottieColorUtils.applyDynamicColors(iconView.context, iconView)
+ iconView.updateAsset(
+ "iconAsset",
+ iconAsset,
+ shouldAnimateIconView,
+ activeAuthType
+ )
viewModel.setPreviousIconWasError(showingError)
}
}
@@ -233,17 +140,12 @@
)
.collect { (iconOverlayAsset, shouldAnimateIconOverlay, showingError) ->
if (iconOverlayAsset != -1) {
- iconOverlayView.setIconOverlayFailureListener(iconOverlayAsset)
- iconOverlayView.setAnimation(iconOverlayAsset)
- iconOverlayView.frame = 0
- LottieColorUtils.applyDynamicColors(
- iconOverlayView.context,
- iconOverlayView
+ iconOverlayView.updateAsset(
+ "iconOverlayAsset",
+ iconOverlayAsset,
+ shouldAnimateIconOverlay,
+ AuthType.Fingerprint
)
-
- if (shouldAnimateIconOverlay) {
- iconOverlayView.playAnimation()
- }
viewModel.setPreviousIconOverlayWasError(showingError)
}
}
@@ -313,11 +215,11 @@
R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft to
"biometricprompt_symbol_fingerprint_to_success_portrait_topleft",
// Face assets
- R.drawable.face_dialog_wink_from_dark to "face_dialog_wink_from_dark",
- R.drawable.face_dialog_dark_to_checkmark to "face_dialog_dark_to_checkmark",
- R.drawable.face_dialog_dark_to_error to "face_dialog_dark_to_error",
- R.drawable.face_dialog_error_to_idle to "face_dialog_error_to_idle",
- R.drawable.face_dialog_idle_static to "face_dialog_idle_static",
+ R.raw.face_dialog_wink_from_dark to "face_dialog_wink_from_dark",
+ R.raw.face_dialog_dark_to_checkmark to "face_dialog_dark_to_checkmark",
+ R.raw.face_dialog_dark_to_error to "face_dialog_dark_to_error",
+ R.raw.face_dialog_error_to_idle to "face_dialog_error_to_idle",
+ R.raw.face_dialog_idle_static to "face_dialog_idle_static",
R.raw.face_dialog_authenticating to "face_dialog_authenticating",
// Co-ex assets
R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie to
@@ -332,31 +234,35 @@
return assetIdToString.getOrDefault(id, "Asset $id not found")
}
-private fun LottieAnimationView.setIconFailureListener(iconAsset: Int, activeAuthType: AuthType) {
- setFailureListener(
- LottieListener<Throwable> { result: Throwable? ->
- Log.d(
- TAG,
- "Collecting iconAsset | " +
- "activeAuthType = $activeAuthType | " +
- "Invalid resource id: $iconAsset, " +
- "name ${getAssetNameFromId(iconAsset)}",
- result
- )
+fun LottieAnimationView.updateAsset(
+ type: String,
+ asset: Int,
+ shouldAnimateIconView: Boolean,
+ activeAuthType: AuthType
+) {
+ setFailureListener(type, asset, activeAuthType)
+ setAnimation(asset)
+ frame = 0
+ if (shouldAnimateIconView) {
+ if (asset == R.raw.face_dialog_authenticating) {
+ loop(true)
+ } else {
+ loop(false)
}
- )
+ playAnimation()
+ }
+ LottieColorUtils.applyDynamicColors(context, this)
}
-private fun LottieAnimationView.setIconOverlayFailureListener(iconOverlayAsset: Int) {
- setFailureListener(
- LottieListener<Throwable> { result: Throwable? ->
- Log.d(
- TAG,
- "Collecting iconOverlayAsset | " +
- "Invalid resource id: $iconOverlayAsset, " +
- "name ${getAssetNameFromId(iconOverlayAsset)}",
- result
- )
- }
- )
+private fun LottieAnimationView.setFailureListener(type: String, asset: Int, authType: AuthType) {
+ setFailureListener { result: Throwable? ->
+ Log.d(
+ TAG,
+ "Collecting $type | " +
+ "activeAuthType = $authType | " +
+ "Invalid resource id: $asset, " +
+ "name ${getAssetNameFromId(asset)}",
+ result
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 9cc4650..9578da4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -139,6 +139,11 @@
overlayView!!.visibility = View.INVISIBLE
Log.d(TAG, "show(): adding overlayView $overlayView")
windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
+ overlayView!!.announceForAccessibility(
+ applicationContext.resources.getString(
+ R.string.accessibility_side_fingerprint_indicator_label
+ )
+ )
}
/** Hide the side fingerprint sensor indicator */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 7081661..6c83dac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -17,11 +17,9 @@
package com.android.systemui.biometrics.ui.viewmodel
-import android.annotation.DrawableRes
import android.annotation.RawRes
import android.content.res.Configuration
import android.graphics.Rect
-import android.hardware.face.Face
import android.util.RotationUtils
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -137,7 +135,7 @@
displayStateInteractor.currentRotation,
displayStateInteractor.isFolded,
displayStateInteractor.isInRearDisplayMode,
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
promptViewModel.isAuthenticated,
promptViewModel.isAuthenticating,
promptViewModel.showingError
@@ -183,7 +181,7 @@
displayStateInteractor.currentRotation,
displayStateInteractor.isFolded,
displayStateInteractor.isInRearDisplayMode,
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
promptViewModel.isAuthenticated,
promptViewModel.isAuthenticating,
promptViewModel.isPendingConfirmation,
@@ -268,7 +266,7 @@
}
}
- @DrawableRes
+ @RawRes
private fun getFaceIconViewAsset(
authState: PromptAuthState,
isAuthenticating: Boolean,
@@ -276,17 +274,17 @@
showingError: Boolean
): Int =
if (authState.isAuthenticated && isPendingConfirmation) {
- R.drawable.face_dialog_wink_from_dark
+ R.raw.face_dialog_wink_from_dark
} else if (authState.isAuthenticated) {
- R.drawable.face_dialog_dark_to_checkmark
+ R.raw.face_dialog_dark_to_checkmark
} else if (isAuthenticating) {
R.raw.face_dialog_authenticating
} else if (showingError) {
- R.drawable.face_dialog_dark_to_error
+ R.raw.face_dialog_dark_to_error
} else if (_previousIconWasError.value) {
- R.drawable.face_dialog_error_to_idle
+ R.raw.face_dialog_error_to_idle
} else {
- R.drawable.face_dialog_idle_static
+ R.raw.face_dialog_idle_static
}
@RawRes
@@ -330,7 +328,7 @@
AuthType.Coex ->
combine(
displayStateInteractor.currentRotation,
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
promptViewModel.isAuthenticated,
promptViewModel.isAuthenticating,
promptViewModel.showingError
@@ -430,7 +428,7 @@
AuthType.Fingerprint,
AuthType.Coex ->
combine(
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
promptViewModel.isAuthenticated,
promptViewModel.isAuthenticating,
promptViewModel.isPendingConfirmation,
@@ -508,7 +506,7 @@
when (activeAuthType) {
AuthType.Fingerprint ->
combine(
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
promptViewModel.isAuthenticated,
promptViewModel.isAuthenticating,
promptViewModel.showingError
@@ -546,7 +544,7 @@
}
AuthType.Coex ->
combine(
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
promptViewModel.isAuthenticated,
promptViewModel.isAuthenticating,
promptViewModel.isPendingConfirmation,
@@ -606,7 +604,7 @@
AuthType.Fingerprint,
AuthType.Coex ->
combine(
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
promptViewModel.isAuthenticated,
promptViewModel.isAuthenticating,
promptViewModel.showingError
@@ -642,7 +640,7 @@
AuthType.Fingerprint,
AuthType.Coex ->
combine(
- promptSelectorInteractor.sensorType,
+ promptSelectorInteractor.fingerprintSensorType,
displayStateInteractor.currentRotation
) { sensorType: FingerprintSensorType, rotation: DisplayRotation ->
when (sensorType) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index ac8807d..3904ee1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -50,6 +50,7 @@
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.biometrics.shared.model.PromptKind
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
@@ -132,11 +133,11 @@
R.dimen.biometric_prompt_landscape_medium_horizontal_padding
)
+ val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
+ udfpsOverlayInteractor.udfpsOverlayParams
+
private val udfpsSensorBounds: Flow<Rect> =
- combine(
- udfpsOverlayInteractor.udfpsOverlayParams,
- displayStateInteractor.currentRotation
- ) { params, rotation ->
+ combine(udfpsOverlayParams, displayStateInteractor.currentRotation) { params, rotation ->
val rotatedBounds = Rect(params.sensorBounds)
RotationUtils.rotateBounds(
rotatedBounds,
@@ -153,32 +154,27 @@
}
.distinctUntilChanged()
+ private val udfpsSensorWidth: Flow<Int> = udfpsOverlayParams.map { it.sensorBounds.width() }
+ private val udfpsSensorHeight: Flow<Int> = udfpsOverlayParams.map { it.sensorBounds.height() }
+
val legacyFingerprintSensorWidth: Flow<Int> =
- combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
- ->
+ combine(modalities, udfpsSensorWidth) { modalities, udfpsSensorWidth ->
if (modalities.hasUdfps) {
- overlayParams.sensorBounds.width()
+ udfpsSensorWidth
} else {
fingerprintIconWidth
}
}
val legacyFingerprintSensorHeight: Flow<Int> =
- combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
- ->
+ combine(modalities, udfpsSensorHeight) { modalities, udfpsSensorHeight ->
if (modalities.hasUdfps) {
- overlayParams.sensorBounds.height()
+ udfpsSensorHeight
} else {
fingerprintIconHeight
}
}
- val fingerprintSensorWidth: Int =
- udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds.width()
-
- val fingerprintSensorHeight: Int =
- udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds.height()
-
private val _accessibilityHint = MutableSharedFlow<String>()
/** Hint for talkback directional guidance */
@@ -442,12 +438,16 @@
/** The size of the biometric icon */
val iconSize: Flow<Pair<Int, Int>> =
- combine(iconViewModel.activeAuthType, modalities) { activeAuthType, modalities ->
+ combine(iconViewModel.activeAuthType, modalities, udfpsSensorWidth, udfpsSensorHeight) {
+ activeAuthType,
+ modalities,
+ udfpsSensorWidth,
+ udfpsSensorHeight ->
if (activeAuthType == PromptIconViewModel.AuthType.Face) {
Pair(faceIconWidth, faceIconHeight)
} else {
if (modalities.hasUdfps) {
- Pair(fingerprintSensorWidth, fingerprintSensorHeight)
+ Pair(udfpsSensorWidth, udfpsSensorHeight)
} else {
Pair(fingerprintIconWidth, fingerprintIconHeight)
}
@@ -921,13 +921,13 @@
udfpsUtils.getTouchInNativeCoordinates(
event.getPointerId(0),
event,
- udfpsOverlayInteractor.udfpsOverlayParams.value
+ udfpsOverlayParams.value
)
if (
!udfpsUtils.isWithinSensorArea(
event.getPointerId(0),
event,
- udfpsOverlayInteractor.udfpsOverlayParams.value
+ udfpsOverlayParams.value
)
) {
_accessibilityHint.emit(
@@ -936,7 +936,7 @@
context,
scaledTouch.x,
scaledTouch.y,
- udfpsOverlayInteractor.udfpsOverlayParams.value
+ udfpsOverlayParams.value
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
index 7d3075a..ed931bd 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
@@ -43,10 +43,11 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -68,7 +69,12 @@
mobileConnectionsRepository: MobileConnectionsRepository,
) {
val subId: StateFlow<Int> = repository.subscriptionId
- val isAnySimSecure: Flow<Boolean> = mobileConnectionsRepository.isAnySimSecure
+ val isAnySimSecure: StateFlow<Boolean> =
+ mobileConnectionsRepository.isAnySimSecure.stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = mobileConnectionsRepository.getIsAnySimSecure(),
+ )
val isLockedEsim: StateFlow<Boolean?> = repository.isLockedEsim
val errorDialogMessage: StateFlow<String?> = repository.errorDialogMessage
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
index 09bf04c..9cb26f3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
@@ -20,9 +20,10 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.util.time.SystemClock;
-import java.util.ArrayList;
+import com.google.common.collect.Sets;
+
import java.util.Collection;
-import java.util.List;
+import java.util.Set;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
@@ -52,7 +53,7 @@
private final SystemClock mSystemClock;
DelayQueue<CombinedResult> mResults = new DelayQueue<>();
- private final List<BeliefListener> mBeliefListeners = new ArrayList<>();
+ private final Set<BeliefListener> mBeliefListeners = Sets.newConcurrentHashSet();
@Inject
HistoryTracker(SystemClock systemClock) {
@@ -161,11 +162,15 @@
}
void addBeliefListener(BeliefListener listener) {
- mBeliefListeners.add(listener);
+ if (listener != null) {
+ mBeliefListeners.add(listener);
+ }
}
void removeBeliefListener(BeliefListener listener) {
- mBeliefListeners.remove(listener);
+ if (listener != null) {
+ mBeliefListeners.remove(listener);
+ }
}
/**
* Represents a falsing score combing all the classifiers together.
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
index 108e22b..64dedea 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.common.ui.binder
+import android.view.View
import android.widget.ImageView
import com.android.systemui.common.shared.model.Icon
@@ -30,4 +31,13 @@
is Icon.Resource -> view.setImageResource(icon.res)
}
}
+
+ fun bindNullable(icon: Icon?, view: ImageView) {
+ if (icon != null) {
+ view.visibility = View.VISIBLE
+ bind(icon, view)
+ } else {
+ view.visibility = View.GONE
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 88c3f9f6..e31f1ad 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -18,6 +18,7 @@
import android.provider.Settings
import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -91,8 +92,8 @@
keyguardTransitionInteractor.startedKeyguardTransitionStep
.mapLatest(::determineSceneAfterTransition)
.filterNotNull()
- .onEach { nextScene ->
- communalSceneInteractor.changeScene(nextScene, CommunalTransitionKeys.SimpleFade)
+ .onEach { (nextScene, nextTransition) ->
+ communalSceneInteractor.changeScene(nextScene, nextTransition)
}
.launchIn(applicationScope)
@@ -188,7 +189,7 @@
private suspend fun determineSceneAfterTransition(
lastStartedTransition: TransitionStep,
- ): SceneKey? {
+ ): Pair<SceneKey, TransitionKey>? {
val to = lastStartedTransition.to
val from = lastStartedTransition.from
val docked = dockManager.isDocked
@@ -201,22 +202,27 @@
// underneath the hub is shown. When launching activities over lockscreen, we only
// change scenes once the activity launch animation is finished, so avoid
// changing the scene here.
- CommunalScenes.Blank
+ Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
}
to == KeyguardState.GLANCEABLE_HUB && from == KeyguardState.OCCLUDED -> {
// When transitioning to the hub from an occluded state, fade out the hub without
// doing any translation.
- CommunalScenes.Communal
+ Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade)
}
// Transitioning to Blank scene when entering the edit mode will be handled separately
// with custom animations.
to == KeyguardState.GONE && !communalInteractor.editModeOpen.value ->
- CommunalScenes.Blank
+ Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
!docked && !KeyguardState.deviceIsAwakeInState(to) -> {
// If the user taps the screen and wakes the device within this timeout, we don't
// want to dismiss the hub
delay(AWAKE_DEBOUNCE_DELAY)
- CommunalScenes.Blank
+ Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
+ }
+ from == KeyguardState.DOZING && to == KeyguardState.GLANCEABLE_HUB -> {
+ // Make sure the communal hub is showing (immediately, not fading in) when
+ // transitioning from dozing to hub.
+ Pair(CommunalScenes.Communal, CommunalTransitionKeys.Immediately)
}
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
index b27fcfc..d8067b8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -27,26 +27,17 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.log.dagger.CommunalTableLog
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.settings.UserFileManager
-import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
/**
@@ -56,10 +47,16 @@
interface CommunalPrefsRepository {
/** Whether the CTA tile has been dismissed. */
- val isCtaDismissed: Flow<Boolean>
+ fun isCtaDismissed(user: UserInfo): Flow<Boolean>
+
+ /** Whether the lock screen widget disclaimer has been dismissed by the user. */
+ fun isDisclaimerDismissed(user: UserInfo): Flow<Boolean>
/** Save the CTA tile dismissed state for the current user. */
- suspend fun setCtaDismissedForCurrentUser()
+ suspend fun setCtaDismissed(user: UserInfo)
+
+ /** Save the lock screen widget disclaimer dismissed state for the current user. */
+ suspend fun setDisclaimerDismissed(user: UserInfo)
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -67,75 +64,43 @@
class CommunalPrefsRepositoryImpl
@Inject
constructor(
- @Background private val backgroundScope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
- private val userRepository: UserRepository,
private val userFileManager: UserFileManager,
broadcastDispatcher: BroadcastDispatcher,
@CommunalLog logBuffer: LogBuffer,
- @CommunalTableLog tableLogBuffer: TableLogBuffer,
) : CommunalPrefsRepository {
+ private val logger by lazy { Logger(logBuffer, TAG) }
- private val logger = Logger(logBuffer, "CommunalPrefsRepositoryImpl")
+ override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
+ readKeyForUser(user, CTA_DISMISSED_STATE)
+
+ override fun isDisclaimerDismissed(user: UserInfo): Flow<Boolean> =
+ readKeyForUser(user, DISCLAIMER_DISMISSED_STATE)
/**
- * Emits an event each time a Backup & Restore restoration job is completed. Does not emit an
- * initial value.
+ * Emits an event each time a Backup & Restore restoration job is completed, and once at the
+ * start of collection.
*/
private val backupRestorationEvents: Flow<Unit> =
- broadcastDispatcher.broadcastFlow(
- filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
- flags = Context.RECEIVER_NOT_EXPORTED,
- permission = BackupHelper.PERMISSION_SELF,
- )
-
- override val isCtaDismissed: Flow<Boolean> =
- combine(
- userRepository.selectedUserInfo,
- // Make sure combine can emit even if we never get a Backup & Restore event,
- // which is the most common case as restoration only happens on initial device
- // setup.
- backupRestorationEvents.emitOnStart().onEach {
- logger.i("Restored state for communal preferences.")
- },
- ) { user, _ ->
- user
- }
- .flatMapLatest(::observeCtaDismissState)
- .logDiffsForTable(
- tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
- columnName = "isCtaDismissed",
- initialValue = false,
+ broadcastDispatcher
+ .broadcastFlow(
+ filter = IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
+ flags = Context.RECEIVER_NOT_EXPORTED,
+ permission = BackupHelper.PERMISSION_SELF,
)
- .stateIn(
- scope = backgroundScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
+ .onEach { logger.i("Restored state for communal preferences.") }
+ .emitOnStart()
- override suspend fun setCtaDismissedForCurrentUser() =
+ override suspend fun setCtaDismissed(user: UserInfo) =
withContext(bgDispatcher) {
- getSharedPrefsForUser(userRepository.getSelectedUserInfo())
- .edit()
- .putBoolean(CTA_DISMISSED_STATE, true)
- .apply()
-
+ getSharedPrefsForUser(user).edit().putBoolean(CTA_DISMISSED_STATE, true).apply()
logger.i("Dismissed CTA tile")
}
- private fun observeCtaDismissState(user: UserInfo): Flow<Boolean> =
- getSharedPrefsForUser(user)
- .observe()
- // Emit at the start of collection to ensure we get an initial value
- .onStart { emit(Unit) }
- .map { getCtaDismissedState() }
- .flowOn(bgDispatcher)
-
- private suspend fun getCtaDismissedState(): Boolean =
+ override suspend fun setDisclaimerDismissed(user: UserInfo) =
withContext(bgDispatcher) {
- getSharedPrefsForUser(userRepository.getSelectedUserInfo())
- .getBoolean(CTA_DISMISSED_STATE, false)
+ getSharedPrefsForUser(user).edit().putBoolean(DISCLAIMER_DISMISSED_STATE, true).apply()
+ logger.i("Dismissed widget disclaimer")
}
private fun getSharedPrefsForUser(user: UserInfo): SharedPreferences {
@@ -146,9 +111,19 @@
)
}
+ private fun readKeyForUser(user: UserInfo, key: String): Flow<Boolean> {
+ return backupRestorationEvents
+ .flatMapLatest {
+ val sharedPrefs = getSharedPrefsForUser(user)
+ sharedPrefs.observe().emitOnStart().map { sharedPrefs.getBoolean(key, false) }
+ }
+ .flowOn(bgDispatcher)
+ }
+
companion object {
- const val TAG = "CommunalRepository"
+ const val TAG = "CommunalPrefsRepository"
const val FILE_NAME = "communal_hub_prefs"
const val CTA_DISMISSED_STATE = "cta_dismissed"
+ const val DISCLAIMER_DISMISSED_STATE = "disclaimer_dismissed"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 00678a8..9f3ade9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -28,7 +28,6 @@
import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.repository.CommunalMediaRepository
-import com.android.systemui.communal.data.repository.CommunalPrefsRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent
@@ -99,7 +98,7 @@
@Background val bgDispatcher: CoroutineDispatcher,
broadcastDispatcher: BroadcastDispatcher,
private val widgetRepository: CommunalWidgetRepository,
- private val communalPrefsRepository: CommunalPrefsRepository,
+ private val communalPrefsInteractor: CommunalPrefsInteractor,
private val mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
keyguardInteractor: KeyguardInteractor,
@@ -325,7 +324,7 @@
}
/** Dismiss the CTA tile from the hub in view mode. */
- suspend fun dismissCtaTile() = communalPrefsRepository.setCtaDismissedForCurrentUser()
+ suspend fun dismissCtaTile() = communalPrefsInteractor.setCtaDismissed()
/** Add a widget at the specified position. */
fun addWidget(
@@ -461,7 +460,7 @@
/** CTA tile to be displayed in the glanceable hub (view mode). */
val ctaTileContent: Flow<List<CommunalContentModel.CtaTileInViewMode>> =
- communalPrefsRepository.isCtaDismissed.map { isDismissed ->
+ communalPrefsInteractor.isCtaDismissed.map { isDismissed ->
if (isDismissed) emptyList() else listOf(CommunalContentModel.CtaTileInViewMode())
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
new file mode 100644
index 0000000..3517650
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.content.pm.UserInfo
+import com.android.app.tracing.coroutines.launch
+import com.android.systemui.communal.data.repository.CommunalPrefsRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.dagger.CommunalTableLog
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.stateIn
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalPrefsInteractor
+@Inject
+constructor(
+ @Background private val bgScope: CoroutineScope,
+ private val repository: CommunalPrefsRepository,
+ userInteractor: SelectedUserInteractor,
+ private val userTracker: UserTracker,
+ @CommunalTableLog tableLogBuffer: TableLogBuffer
+) {
+
+ val isCtaDismissed: Flow<Boolean> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.isCtaDismissed(user) }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ columnName = "isCtaDismissed",
+ initialValue = false,
+ )
+ .stateIn(
+ scope = bgScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ suspend fun setCtaDismissed(user: UserInfo = userTracker.userInfo) =
+ repository.setCtaDismissed(user)
+
+ val isDisclaimerDismissed: Flow<Boolean> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.isDisclaimerDismissed(user) }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ columnName = "isDisclaimerDismissed",
+ initialValue = false,
+ )
+ .stateIn(
+ scope = bgScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ fun setDisclaimerDismissed(user: UserInfo = userTracker.userInfo) {
+ bgScope.launch("$TAG#setDisclaimerDismissed") { repository.setDisclaimerDismissed(user) }
+ }
+
+ private companion object {
+ const val TAG = "CommunalPrefsInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 20d8a2a..fd540c4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.communal.data.repository.CommunalSceneRepository
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -60,14 +61,14 @@
communalSceneRepository.snapToScene(newScene, delayMillis)
}
- /** Immediately snaps to the new scene when activity is started. */
- fun snapToSceneForActivityStart(newScene: SceneKey, delayMillis: Long = 0) {
+ /** Changes to Blank scene when starting an activity after dismissing keyguard. */
+ fun changeSceneForActivityStartOnDismissKeyguard() {
// skip if we're starting edit mode activity, as it will be handled later by changeScene
// with transition key [CommunalTransitionKeys.ToEditMode].
if (_editModeState.value == EditModeState.STARTING) {
return
}
- snapToScene(newScene, delayMillis)
+ changeScene(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
}
/**
@@ -144,8 +145,14 @@
*
* This flow will be true during any transition and when idle on the communal scene.
*/
- val isCommunalVisible: Flow<Boolean> =
- transitionState.map {
- !(it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Blank)
- }
+ val isCommunalVisible: StateFlow<Boolean> =
+ transitionState
+ .map {
+ !(it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Blank)
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index cc90730..6ec6ec1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -129,7 +129,6 @@
/** Called as the UI requests opening the widget editor with an optional preselected widget. */
open fun onOpenWidgetEditor(
- preselectedKey: String? = null,
shouldOpenWidgetPickerOnStart: Boolean = false,
) {}
@@ -146,7 +145,7 @@
open fun onReorderWidgetCancel() {}
/** Called as the user request to show the customize widget button. */
- open fun onShowCustomizeWidgetButton() {}
+ open fun onLongClick() {}
/** Set the key of the currently selected item */
fun setSelectedKey(key: String?) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index c0c5861..9185384 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -26,6 +26,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags.enableWidgetPickerSizeFilter
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -42,6 +43,7 @@
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineDispatcher
@@ -67,6 +69,7 @@
private val uiEventLogger: UiEventLogger,
@CommunalLog logBuffer: LogBuffer,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val communalPrefsInteractor: CommunalPrefsInteractor,
) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
private val logger = Logger(logBuffer, "CommunalEditModeViewModel")
@@ -76,9 +79,16 @@
override val isCommunalContentVisible: Flow<Boolean> =
communalSceneInteractor.editModeState.map { it == EditModeState.SHOWING }
+ val showDisclaimer: Flow<Boolean> =
+ allOf(isCommunalContentVisible, not(communalPrefsInteractor.isDisclaimerDismissed))
+
+ fun onDisclaimerDismissed() {
+ communalPrefsInteractor.setDisclaimerDismissed()
+ }
+
/**
- * Emits when edit mode activity can show, after we've transitioned to [KeyguardState.GONE]
- * and edit mode is open.
+ * Emits when edit mode activity can show, after we've transitioned to [KeyguardState.GONE] and
+ * edit mode is open.
*/
val canShowEditMode =
allOf(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 11247df..2043dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -40,6 +40,7 @@
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
@@ -76,9 +77,10 @@
@Main private val resources: Resources,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
keyguardInteractor: KeyguardInteractor,
+ private val keyguardIndicationController: KeyguardIndicationController,
communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
- private val communalSettingsInteractor: CommunalSettingsInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
tutorialInteractor: CommunalTutorialInteractor,
private val shadeInteractor: ShadeInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@@ -218,9 +220,8 @@
}
override fun onOpenWidgetEditor(
- preselectedKey: String?,
shouldOpenWidgetPickerOnStart: Boolean,
- ) = communalInteractor.showWidgetEditor(preselectedKey, shouldOpenWidgetPickerOnStart)
+ ) = communalInteractor.showWidgetEditor(selectedKey.value, shouldOpenWidgetPickerOnStart)
override fun onDismissCtaTile() {
scope.launch {
@@ -229,7 +230,11 @@
}
}
- override fun onShowCustomizeWidgetButton() {
+ fun onClick() {
+ keyguardIndicationController.showActionToUnlock()
+ }
+
+ override fun onLongClick() {
setCurrentPopupType(PopupType.CustomizeWidgetButton)
}
@@ -319,7 +324,7 @@
}
sealed class PopupType {
- object CtaTile : PopupType()
+ data object CtaTile : PopupType()
- object CustomizeWidgetButton : PopupType()
+ data object CustomizeWidgetButton : PopupType()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 40df6cec..46f802f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -96,7 +96,8 @@
run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
}
}
- } ?: run { Log.w(TAG, "No data in result.") }
+ }
+ ?: run { Log.w(TAG, "No data in result.") }
}
else ->
Log.w(
@@ -127,7 +128,7 @@
Box(
modifier =
Modifier.fillMaxSize()
- .background(LocalAndroidColorScheme.current.outlineVariant),
+ .background(LocalAndroidColorScheme.current.onSecondaryFixed),
) {
CommunalHub(
viewModel = communalViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 9f0fc51..9ae63a1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -25,7 +25,9 @@
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.CoreStartable;
import com.android.systemui.ScreenDecorationsModule;
+import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.accessibility.SystemActionsModule;
+import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.battery.BatterySaverModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
@@ -107,6 +109,8 @@
* SystemUI code that variants of SystemUI _must_ include to function correctly.
*/
@Module(includes = {
+ AccessibilityModule.class,
+ AccessibilityRepositoryModule.class,
AospPolicyModule.class,
BatterySaverModule.class,
CollapsedStatusBarFragmentStartableModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a7ff3c3..4b2fb44 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -32,8 +32,6 @@
import com.android.systemui.CameraProtectionModule;
import com.android.systemui.CoreStartable;
import com.android.systemui.SystemUISecondaryUserService;
-import com.android.systemui.accessibility.AccessibilityModule;
-import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.ambient.dagger.AmbientModule;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
@@ -187,8 +185,6 @@
* may not appreciate that.
*/
@Module(includes = {
- AccessibilityModule.class,
- AccessibilityRepositoryModule.class,
AmbientModule.class,
AppOpsModule.class,
AssistModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepository.kt
new file mode 100644
index 0000000..250b432
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.deviceconfig.data.repository
+
+import android.provider.DeviceConfig
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
+
+@SysUISingleton
+class DeviceConfigRepository
+@Inject
+constructor(
+ @Background private val backgroundExecutor: Executor,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val dataSource: DeviceConfigProxy,
+) {
+
+ fun property(namespace: String, name: String, default: Boolean): Flow<Boolean> {
+ return conflatedCallbackFlow {
+ val listener = { properties: DeviceConfig.Properties ->
+ if (properties.keyset.contains(name)) {
+ trySend(properties.getBoolean(name, default))
+ }
+ }
+
+ dataSource.addOnPropertiesChangedListener(
+ namespace,
+ backgroundExecutor,
+ listener,
+ )
+ trySend(dataSource.getBoolean(namespace, name, default))
+
+ awaitClose { dataSource.removeOnPropertiesChangedListener(listener) }
+ }
+ .flowOn(backgroundDispatcher)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractor.kt
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
copy to packages/SystemUI/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractor.kt
index 97ceacc..d04f8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractor.kt
@@ -14,13 +14,21 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.deviceconfig.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.deviceconfig.data.repository.DeviceConfigRepository
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
@SysUISingleton
-class NoopConsistencyInteractor @Inject constructor() : GridTypeConsistencyInteractor {
- override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> = tiles
+class DeviceConfigInteractor
+@Inject
+constructor(
+ private val repository: DeviceConfigRepository,
+) {
+
+ fun property(namespace: String, name: String, default: Boolean): Flow<Boolean> {
+ return repository.property(namespace, name, default)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 10d6881..4c7142b 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -211,6 +211,9 @@
}
}
+ /** Whether the device is in lockdown mode, where bouncer input is required to unlock. */
+ val isInLockdown: Flow<Boolean> = deviceEntryRestrictionReason.map { it.isInLockdown() }
+
/**
* Attempt to enter the device and dismiss the lockscreen. If authentication is required to
* unlock the device it will transition to bouncer.
@@ -259,6 +262,27 @@
return repository.isLockscreenEnabled()
}
+ fun DeviceEntryRestrictionReason?.isInLockdown(): Boolean {
+ return when (this) {
+ DeviceEntryRestrictionReason.UserLockdown -> true
+ DeviceEntryRestrictionReason.PolicyLockdown -> true
+
+ // Add individual enum value instead of using "else" so new reasons are guaranteed
+ // to be added here at compile-time.
+ null -> false
+ DeviceEntryRestrictionReason.DeviceNotUnlockedSinceReboot -> false
+ DeviceEntryRestrictionReason.BouncerLockedOut -> false
+ DeviceEntryRestrictionReason.AdaptiveAuthRequest -> false
+ DeviceEntryRestrictionReason.NonStrongBiometricsSecurityTimeout -> false
+ DeviceEntryRestrictionReason.TrustAgentDisabled -> false
+ DeviceEntryRestrictionReason.StrongBiometricsLockedOut -> false
+ DeviceEntryRestrictionReason.SecurityTimeout -> false
+ DeviceEntryRestrictionReason.DeviceNotUnlockedSinceMainlineUpdate -> false
+ DeviceEntryRestrictionReason.UnattendedUpdate -> false
+ DeviceEntryRestrictionReason.NonStrongFaceLockedOut -> false
+ }
+ }
+
/**
* Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
* dismissed once the authentication challenge is completed. For example, completing a biometric
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 9d6c2bf..0bcfa96 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -47,7 +47,6 @@
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.shareIn
@@ -155,10 +154,11 @@
is DisplayEvent.Changed -> previousIds + id
}
}
- .shareIn(
+ .distinctUntilChanged()
+ .stateIn(
bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- replay = 1
+ SharingStarted.WhileSubscribed(),
+ emptySet(),
)
} else {
oldEnabledDisplays.map { enabledDisplaysSet ->
@@ -177,7 +177,16 @@
enabledDisplayIds
.mapElementsLazily { displayId -> getDisplay(displayId) }
.flowOn(backgroundCoroutineDispatcher)
- .debugLog("enabledDisplayIds")
+ .debugLog("enabledDisplays")
+ .stateIn(
+ bgApplicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue =
+ setOf(
+ getDisplay(Display.DEFAULT_DISPLAY)
+ ?: error("Unable to get default display.")
+ )
+ )
} else {
oldEnabledDisplays
}
@@ -355,20 +364,31 @@
* [createValue] returns a null element, it will not be added in the output set.
*/
private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
- var initialSet = emptySet<T>()
- val currentMap = mutableMapOf<T, V>()
- var resultSet = emptySet<V>()
- return onEach { currentSet ->
- val removed = initialSet - currentSet
- val added = currentSet - initialSet
- if (added.isNotEmpty() || removed.isNotEmpty()) {
- added.forEach { key: T -> createValue(key)?.let { currentMap[key] = it } }
- removed.forEach { key: T -> currentMap.remove(key) }
- resultSet = currentMap.values.toSet() // Creates a **copy** of values
+ data class State<T, V>(
+ val previousSet: Set<T>,
+ // Caches T values from the previousSet that were already converted to V
+ val valueMap: Map<T, V>,
+ val resultSet: Set<V>
+ )
+
+ val initialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
+
+ return this.scan(initialState) { state, currentSet ->
+ if (currentSet == state.previousSet) {
+ state
+ } else {
+ val removed = state.previousSet - currentSet
+ val added = currentSet - state.previousSet
+ val newMap = state.valueMap.toMutableMap()
+
+ added.forEach { key -> createValue(key)?.let { newMap[key] = it } }
+ removed.forEach { key -> newMap.remove(key) }
+
+ val resultSet = newMap.values.toSet()
+ State(currentSet, newMap, resultSet)
}
- initialSet = currentSet
}
- .map { resultSet }
+ .map { it.resultSet }
}
private companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index 73b7a8a..e4b290d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -85,7 +85,7 @@
class ConnectedDisplayInteractorImpl
@Inject
constructor(
- private val virtualDeviceManager: VirtualDeviceManager,
+ private val virtualDeviceManager: VirtualDeviceManager?,
keyguardRepository: KeyguardRepository,
displayRepository: DisplayRepository,
deviceStateRepository: DeviceStateRepository,
@@ -156,6 +156,7 @@
private fun isVirtualDeviceOwnedMirrorDisplay(display: Display): Boolean {
return Flags.interactiveScreenMirror() &&
+ virtualDeviceManager != null &&
virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(display.displayId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index 7b5139a..6cbe29e 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -160,7 +160,7 @@
}
fun onTileClick(): Boolean {
- if (state == State.TIMEOUT_WAIT) {
+ if (state == State.TIMEOUT_WAIT || state == State.IDLE) {
setState(State.IDLE)
qsTile?.let {
it.click(expandable)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
index 5635f80..8a40c03 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
@@ -19,7 +19,7 @@
import android.app.Activity
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.keyboardShortcutHelperRewrite
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.ui.ShortcutHelperActivityStarter
import com.android.systemui.keyboard.shortcut.ui.view.ShortcutHelperActivity
import dagger.Binds
@@ -52,8 +52,8 @@
@Provides
@IntoMap
- @ClassKey(ShortcutHelperRepository::class)
- fun repo(implLazy: Lazy<ShortcutHelperRepository>): CoreStartable {
+ @ClassKey(ShortcutHelperStateRepository::class)
+ fun repo(implLazy: Lazy<ShortcutHelperStateRepository>): CoreStartable {
return if (keyboardShortcutHelperRewrite()) {
implLazy.get()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
new file mode 100644
index 0000000..04bde26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.shortcut.data.source.MultitaskingShortcutsSource
+import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import javax.inject.Inject
+
+@SysUISingleton
+class ShortcutHelperCategoriesRepository
+@Inject
+constructor(
+ private val systemShortcutsSource: SystemShortcutsSource,
+ private val multitaskingShortcutsSource: MultitaskingShortcutsSource,
+) {
+
+ fun systemShortcutsCategory(): ShortcutCategory =
+ systemShortcutsSource.systemShortcutsCategory()
+
+ fun multitaskingShortcutsCategory(): ShortcutCategory =
+ multitaskingShortcutsSource.multitaskingShortcutCategory()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt
rename to packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
index ec92ca9..82df95d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
@@ -20,23 +20,34 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.hardware.input.InputManager
import android.os.UserHandle
+import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
import com.android.systemui.CoreStartable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Inactive
+import com.android.systemui.shared.hardware.findInputDevice
import com.android.systemui.statusbar.CommandQueue
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
@SysUISingleton
-class ShortcutHelperRepository
+class ShortcutHelperStateRepository
@Inject
constructor(
private val commandQueue: CommandQueue,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val inputManager: InputManager,
+ @Background private val backgroundScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) : CoreStartable {
val state = MutableStateFlow<ShortcutHelperState>(Inactive)
@@ -44,7 +55,9 @@
override fun start() {
registerBroadcastReceiver(
action = Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS,
- onReceive = { state.value = Active() }
+ onReceive = {
+ backgroundScope.launch { state.value = Active(findPhysicalKeyboardId()) }
+ }
)
registerBroadcastReceiver(
action = Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS,
@@ -72,6 +85,13 @@
)
}
+ private suspend fun findPhysicalKeyboardId() =
+ withContext(backgroundDispatcher) {
+ val firstEnabledPhysicalKeyboard =
+ inputManager.findInputDevice { it.isEnabled && it.isFullKeyboard && !it.isVirtual }
+ return@withContext firstEnabledPhysicalKeyboard?.id ?: VIRTUAL_KEYBOARD
+ }
+
fun hide() {
state.value = Inactive
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
new file mode 100644
index 0000000..7b64f87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.keyboard.shortcut.data.source
+
+import android.content.res.Resources
+import android.view.KeyEvent.KEYCODE_DPAD_LEFT
+import android.view.KeyEvent.KEYCODE_DPAD_RIGHT
+import android.view.KeyEvent.KEYCODE_DPAD_UP
+import android.view.KeyEvent.KEYCODE_TAB
+import android.view.KeyEvent.META_ALT_ON
+import android.view.KeyEvent.META_CTRL_ON
+import android.view.KeyEvent.META_META_ON
+import android.view.KeyEvent.META_SHIFT_ON
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MULTI_TASKING
+import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class MultitaskingShortcutsSource @Inject constructor(@Main private val resources: Resources) {
+
+ fun multitaskingShortcutCategory() =
+ shortcutCategory(MULTI_TASKING) {
+ subCategory(
+ resources.getString(R.string.shortcutHelper_category_recent_apps),
+ recentsShortcuts()
+ )
+ subCategory(
+ resources.getString(R.string.shortcutHelper_category_split_screen),
+ splitScreenShortcuts()
+ )
+ }
+
+ private fun splitScreenShortcuts() =
+ listOf(
+ // Enter Split screen with current app to RHS:
+ // - Meta + Ctrl + Right arrow
+ shortcut(resources.getString(R.string.system_multitasking_rhs)) {
+ command(META_META_ON, META_CTRL_ON, KEYCODE_DPAD_RIGHT)
+ },
+ // Enter Split screen with current app to LHS:
+ // - Meta + Ctrl + Left arrow
+ shortcut(resources.getString(R.string.system_multitasking_lhs)) {
+ command(META_META_ON, META_CTRL_ON, KEYCODE_DPAD_LEFT)
+ },
+ // Switch from Split screen to full screen:
+ // - Meta + Ctrl + Up arrow
+ shortcut(resources.getString(R.string.system_multitasking_full_screen)) {
+ command(META_META_ON, META_CTRL_ON, KEYCODE_DPAD_UP)
+ },
+ // Change split screen focus to RHS:
+ // - Meta + Alt + Right arrow
+ shortcut(resources.getString(R.string.system_multitasking_splitscreen_focus_rhs)) {
+ command(META_META_ON, META_ALT_ON, KEYCODE_DPAD_RIGHT)
+ },
+ // Change split screen focus to LHS:
+ // - Meta + Alt + Left arrow
+ shortcut(resources.getString(R.string.system_multitasking_splitscreen_focus_rhs)) {
+ command(META_META_ON, META_ALT_ON, KEYCODE_DPAD_LEFT)
+ },
+ )
+
+ private fun recentsShortcuts() =
+ listOf(
+ // Cycle through recent apps (forward):
+ // - Alt + Tab
+ shortcut(resources.getString(R.string.group_system_cycle_forward)) {
+ command(META_ALT_ON, KEYCODE_TAB)
+ },
+ // Cycle through recent apps (back):
+ // - Shift + Alt + Tab
+ shortcut(resources.getString(R.string.group_system_cycle_back)) {
+ command(META_SHIFT_ON, META_ALT_ON, KEYCODE_TAB)
+ },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
new file mode 100644
index 0000000..51c67df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/SystemShortcutsSource.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.keyboard.shortcut.data.source
+
+import android.content.res.Resources
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.KEYCODE_DEL
+import android.view.KeyEvent.KEYCODE_DPAD_LEFT
+import android.view.KeyEvent.KEYCODE_ENTER
+import android.view.KeyEvent.KEYCODE_ESCAPE
+import android.view.KeyEvent.KEYCODE_H
+import android.view.KeyEvent.KEYCODE_I
+import android.view.KeyEvent.KEYCODE_L
+import android.view.KeyEvent.KEYCODE_N
+import android.view.KeyEvent.KEYCODE_S
+import android.view.KeyEvent.KEYCODE_SLASH
+import android.view.KeyEvent.KEYCODE_TAB
+import android.view.KeyEvent.META_CTRL_ON
+import android.view.KeyEvent.META_META_ON
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.SYSTEM
+import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class SystemShortcutsSource @Inject constructor(@Main private val resources: Resources) {
+
+ fun systemShortcutsCategory() =
+ shortcutCategory(SYSTEM) {
+ subCategory(
+ resources.getString(R.string.shortcut_helper_category_system_controls),
+ systemControlsShortcuts()
+ )
+ subCategory(
+ resources.getString(R.string.shortcut_helper_category_system_apps),
+ systemAppsShortcuts()
+ )
+ }
+
+ private fun systemControlsShortcuts() =
+ listOf(
+ // Access list of all apps and search (i.e. Search/Launcher):
+ // - Meta
+ shortcut(resources.getString(R.string.group_system_access_all_apps_search)) {
+ command(META_META_ON)
+ },
+ // Access home screen:
+ // - Meta + H
+ // - Meta + Enter
+ shortcut(resources.getString(R.string.group_system_access_home_screen)) {
+ command(META_META_ON, KEYCODE_H)
+ command(META_META_ON, KEYCODE_ENTER)
+ },
+ // Overview of open apps:
+ // - Meta + Tab
+ shortcut(resources.getString(R.string.group_system_overview_open_apps)) {
+ command(META_META_ON, KEYCODE_TAB)
+ },
+ // Back: go back to previous state (back button)
+ // - Meta + Escape OR
+ // - Meta + Backspace OR
+ // - Meta + Left arrow
+ shortcut(resources.getString(R.string.group_system_go_back)) {
+ command(META_META_ON, KEYCODE_ESCAPE)
+ command(META_META_ON, KEYCODE_DEL)
+ command(META_META_ON, KEYCODE_DPAD_LEFT)
+ },
+ // Take a full screenshot:
+ // - Meta + Ctrl + S
+ shortcut(resources.getString(R.string.group_system_full_screenshot)) {
+ command(META_META_ON, META_CTRL_ON, KEYCODE_S)
+ },
+ // Access list of system / apps shortcuts:
+ // - Meta + /
+ shortcut(resources.getString(R.string.group_system_access_system_app_shortcuts)) {
+ command(META_META_ON, KEYCODE_SLASH)
+ },
+ // Access notification shade:
+ // - Meta + N
+ shortcut(resources.getString(R.string.group_system_access_notification_shade)) {
+ command(META_META_ON, KEYCODE_N)
+ },
+ // Lock screen:
+ // - Meta + L
+ shortcut(resources.getString(R.string.group_system_lock_screen)) {
+ command(META_META_ON, KEYCODE_L)
+ },
+ )
+
+ private fun systemAppsShortcuts() =
+ listOf(
+ // Pull up Notes app for quick memo:
+ // - Meta + Ctrl + N
+ shortcut(resources.getString(R.string.group_system_quick_memo)) {
+ command(META_META_ON, META_CTRL_ON, KEYCODE_N)
+ },
+ // Access system settings:
+ // - Meta + I
+ shortcut(resources.getString(R.string.group_system_access_system_settings)) {
+ command(META_META_ON, KEYCODE_I)
+ },
+ // Access Assistant:
+ // - Meta + A
+ shortcut(resources.getString(R.string.group_system_access_google_assistant)) {
+ command(META_META_ON, KEYCODE_A)
+ },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
new file mode 100644
index 0000000..883407c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.keyboard.shortcut.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class ShortcutHelperCategoriesInteractor
+@Inject
+constructor(
+ stateRepository: ShortcutHelperStateRepository,
+ categoriesRepository: ShortcutHelperCategoriesRepository,
+) {
+
+ val shortcutCategories: Flow<List<ShortcutCategory>> =
+ stateRepository.state.map { state ->
+ when (state) {
+ is ShortcutHelperState.Active ->
+ listOf(
+ categoriesRepository.systemShortcutsCategory(),
+ categoriesRepository.multitaskingShortcutsCategory()
+ )
+ is ShortcutHelperState.Inactive -> emptyList()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
index 44f1c1e..3d707f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import com.android.systemui.model.SysUiState
import com.android.systemui.settings.DisplayTracker
@@ -29,13 +29,13 @@
import kotlinx.coroutines.launch
@SysUISingleton
-class ShortcutHelperInteractor
+class ShortcutHelperStateInteractor
@Inject
constructor(
private val displayTracker: DisplayTracker,
@Background private val backgroundScope: CoroutineScope,
private val sysUiState: SysUiState,
- private val repository: ShortcutHelperRepository
+ private val repository: ShortcutHelperStateRepository
) {
val state: Flow<ShortcutHelperState> = repository.state
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
new file mode 100644
index 0000000..e5b870a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.shared.model
+
+data class Shortcut(val label: String, val commands: List<ShortcutCommand>)
+
+class ShortcutBuilder(private val label: String) {
+ val commands = mutableListOf<ShortcutCommand>()
+
+ fun command(vararg keyCodes: Int) {
+ commands += ShortcutCommand(keyCodes.toList())
+ }
+
+ fun build() = Shortcut(label, commands)
+}
+
+fun shortcut(label: String, block: ShortcutBuilder.() -> Unit): Shortcut =
+ ShortcutBuilder(label).apply(block).build()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
new file mode 100644
index 0000000..c5e8d2c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.shared.model
+
+enum class ShortcutCategoryType {
+ SYSTEM,
+ MULTI_TASKING,
+}
+
+data class ShortcutCategory(
+ val type: ShortcutCategoryType,
+ val subCategories: List<ShortcutSubCategory>
+)
+
+class ShortcutCategoryBuilder(val type: ShortcutCategoryType) {
+ private val subCategories = mutableListOf<ShortcutSubCategory>()
+
+ fun subCategory(label: String, shortcuts: List<Shortcut>) {
+ subCategories += ShortcutSubCategory(label, shortcuts)
+ }
+
+ fun build() = ShortcutCategory(type, subCategories)
+}
+
+fun shortcutCategory(type: ShortcutCategoryType, block: ShortcutCategoryBuilder.() -> Unit) =
+ ShortcutCategoryBuilder(type).apply(block).build()
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
similarity index 83%
copy from services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
copy to packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
index bb8db8a..a98a8ff 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package com.android.server.dreams;
+package com.android.systemui.keyboard.shortcut.shared.model
-public class TestDreamSettingsActivity {
-}
+data class ShortcutCommand(val keyCodes: List<Int>)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt
index d22d6c8..1f22141 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutHelperState.kt
@@ -19,5 +19,5 @@
sealed interface ShortcutHelperState {
data object Inactive : ShortcutHelperState
- data class Active(val deviceId: Int? = null) : ShortcutHelperState
+ data class Active(val deviceId: Int) : ShortcutHelperState
}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
similarity index 80%
copy from services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
copy to packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
index bb8db8a..4545b4c 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package com.android.server.dreams;
+package com.android.systemui.keyboard.shortcut.shared.model
-public class TestDreamSettingsActivity {
-}
+data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index c623f5c..510e552 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyboard.shortcut.ui.viewmodel
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperInteractor
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -29,20 +29,20 @@
@Inject
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
- private val interactor: ShortcutHelperInteractor
+ private val stateInteractor: ShortcutHelperStateInteractor,
) {
val shouldShow =
- interactor.state
+ stateInteractor.state
.map { it is ShortcutHelperState.Active }
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
fun onViewClosed() {
- interactor.onViewClosed()
+ stateInteractor.onViewClosed()
}
fun onViewOpened() {
- interactor.onViewOpened()
+ stateInteractor.onViewOpened()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 209bc7a..c4b70d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -89,6 +89,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.ScreenPowerState;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.domain.startable.KeyguardStateCallbackStartable;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.settings.DisplayTracker;
@@ -122,6 +123,7 @@
private final KeyguardInteractor mKeyguardInteractor;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
private final Executor mMainExecutor;
+ private final Lazy<KeyguardStateCallbackStartable> mKeyguardStateCallbackStartableLazy;
private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
@@ -341,7 +343,8 @@
Lazy<SceneInteractor> sceneInteractorLazy,
@Main Executor mainExecutor,
KeyguardInteractor keyguardInteractor,
- KeyguardEnabledInteractor keyguardEnabledInteractor) {
+ KeyguardEnabledInteractor keyguardEnabledInteractor,
+ Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -353,6 +356,7 @@
mKeyguardInteractor = keyguardInteractor;
mSceneInteractorLazy = sceneInteractorLazy;
mMainExecutor = mainExecutor;
+ mKeyguardStateCallbackStartableLazy = keyguardStateCallbackStartableLazy;
if (KeyguardWmStateRefactor.isEnabled()) {
WindowManagerLockscreenVisibilityViewBinder.bind(
@@ -440,7 +444,11 @@
public void addStateMonitorCallback(IKeyguardStateCallback callback) {
trace("addStateMonitorCallback");
checkPermission();
- mKeyguardViewMediator.addStateMonitorCallback(callback);
+ if (SceneContainerFlag.isEnabled()) {
+ mKeyguardStateCallbackStartableLazy.get().addCallback(callback);
+ } else {
+ mKeyguardViewMediator.addStateMonitorCallback(callback);
+ }
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 00566c1..a7e2633 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -39,7 +39,7 @@
import com.android.internal.R
import com.android.keyguard.KeyguardClockSwitchController
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.Flags.fastUnlockTransition
+import com.android.systemui.Flags.fasterUnlockTransition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
@@ -363,9 +363,9 @@
}
with(wallpaperCannedUnlockAnimator) {
- duration = if (fastUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
+ duration = if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
else LAUNCHER_ICONS_ANIMATION_DURATION_MS
- interpolator = if (fastUnlockTransition()) Interpolators.LINEAR
+ interpolator = if (fasterUnlockTransition()) Interpolators.LINEAR
else Interpolators.ALPHA_OUT
addUpdateListener { valueAnimator: ValueAnimator ->
setWallpaperAppearAmount(valueAnimator.animatedValue as Float)
@@ -613,7 +613,7 @@
val isWakeAndUnlockNotFromDream = biometricUnlockControllerLazy.get().isWakeAndUnlock &&
biometricUnlockControllerLazy.get().mode != MODE_WAKE_AND_UNLOCK_FROM_DREAM
- val duration = if (fastUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
+ val duration = if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
else LAUNCHER_ICONS_ANIMATION_DURATION_MS
listeners.forEach {
it.onUnlockAnimationStarted(
@@ -1148,7 +1148,7 @@
* TODO (b/298186160) replace references with the constant itself when flag is removed
*/
private fun cannedUnlockStartDelayMs(): Long {
- return if (fastUnlockTransition()) CANNED_UNLOCK_START_DELAY
+ return if (fasterUnlockTransition()) CANNED_UNLOCK_START_DELAY
else LEGACY_CANNED_UNLOCK_START_DELAY
}
@@ -1157,7 +1157,7 @@
* TODO (b/298186160) replace references with the constant itself when flag is removed
*/
private fun unlockAnimationDurationMs(): Long {
- return if (fastUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
+ return if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS
else LEGACY_UNLOCK_ANIMATION_DURATION_MS
}
@@ -1166,7 +1166,7 @@
* TODO (b/298186160) replace references with the constant itself when flag is removed
*/
private fun surfaceBehindFadeOutDurationMs(): Long {
- return if (fastUnlockTransition()) SURFACE_BEHIND_FADE_OUT_DURATION_MS
+ return if (fasterUnlockTransition()) SURFACE_BEHIND_FADE_OUT_DURATION_MS
else LEGACY_SURFACE_BEHIND_SWIPE_FADE_DURATION_MS
}
@@ -1175,7 +1175,7 @@
* TODO (b/298186160) replace references with the constant itself when flag is removed
*/
private fun surfaceBehindFadeOutStartDelayMs(): Long {
- return if (fastUnlockTransition()) SURFACE_BEHIND_FADE_OUT_START_DELAY_MS
+ return if (fasterUnlockTransition()) SURFACE_BEHIND_FADE_OUT_START_DELAY_MS
else LEGACY_UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2d60fcc..36b7ed2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -151,6 +151,7 @@
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -178,6 +179,8 @@
import dagger.Lazy;
+import kotlinx.coroutines.CoroutineDispatcher;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -187,8 +190,6 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-import kotlinx.coroutines.CoroutineDispatcher;
-
/**
* Mediates requests related to the keyguard. This includes queries about the
* state of the keyguard, power management events that effect whether the keyguard
@@ -3502,12 +3503,14 @@
+ " --> flags=0x" + Integer.toHexString(flags));
}
- try {
- mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(),
- mSelectedUserInteractor.getSelectedUserId(true));
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to set disable flags: " + flags, e);
+ if (!SceneContainerFlag.isEnabled()) {
+ try {
+ mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
+ mContext.getPackageName(),
+ mSelectedUserInteractor.getSelectedUserId(true));
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to set disable flags: " + flags, e);
+ }
}
}
}
@@ -3798,6 +3801,10 @@
}
private void notifyDefaultDisplayCallbacks(boolean showing) {
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
+
// TODO(b/140053364)
whitelistIpcs(() -> {
int size = mKeyguardStateCallbacks.size();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index b3d9a76..56b520e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -59,7 +59,7 @@
override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
- override fun pickerName(): String = context.getString(R.string.accessibility_wallet_button)
+ override fun pickerName(): String = context.getString(R.string.wallet_title)
override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 882f231..dd3e619 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -188,35 +188,33 @@
)
private val isFingerprintEnrolled: Flow<Boolean> =
- selectedUserId
- .flatMapLatest { currentUserId ->
- conflatedCallbackFlow {
- val callback =
- object : AuthController.Callback {
- override fun onEnrollmentsChanged(
- sensorBiometricType: BiometricType,
- userId: Int,
- hasEnrollments: Boolean
- ) {
- if (sensorBiometricType.isFingerprint && userId == currentUserId) {
- trySendWithFailureLogging(
- hasEnrollments,
- TAG,
- "update fpEnrollment"
- )
- }
+ selectedUserId.flatMapLatest { currentUserId ->
+ conflatedCallbackFlow {
+ val callback =
+ object : AuthController.Callback {
+ override fun onEnrollmentsChanged(
+ sensorBiometricType: BiometricType,
+ userId: Int,
+ hasEnrollments: Boolean
+ ) {
+ if (sensorBiometricType.isFingerprint && userId == currentUserId) {
+ trySendWithFailureLogging(
+ hasEnrollments,
+ TAG,
+ "update fpEnrollment"
+ )
}
}
- authController.addCallback(callback)
- awaitClose { authController.removeCallback(callback) }
- }
+ }
+ authController.addCallback(callback)
+ trySendWithFailureLogging(
+ authController.isFingerprintEnrolled(currentUserId),
+ TAG,
+ "Initial value of fingerprint enrollment"
+ )
+ awaitClose { authController.removeCallback(callback) }
}
- .stateIn(
- scope,
- started = SharingStarted.Eagerly,
- initialValue =
- authController.isFingerprintEnrolled(userRepository.getSelectedUserInfo().id)
- )
+ }
private val isFaceEnrolled: Flow<Boolean> =
selectedUserId.flatMapLatest { selectedUserId: Int ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index cdf3b06..63dd255 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.repository
import android.graphics.Point
+import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.biometrics.AuthController
@@ -39,6 +40,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.time.SystemClock
@@ -293,6 +295,15 @@
/** Sets whether the keyguard is enabled (see [isKeyguardEnabled]). */
fun setKeyguardEnabled(enabled: Boolean)
+
+ /** @see isShowKeyguardWhenReenabled */
+ fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean)
+
+ /**
+ * Returns `true` if the keyguard should be re-shown once it becomes re-enabled again; `false`
+ * otherwise.
+ */
+ fun isShowKeyguardWhenReenabled(): Boolean
}
/** Encapsulates application state for the keyguard. */
@@ -311,6 +322,7 @@
private val systemClock: SystemClock,
facePropertyRepository: FacePropertyRepository,
private val userTracker: UserTracker,
+ lockPatternUtils: LockPatternUtils,
) : KeyguardRepository {
private val _dismissAction: MutableStateFlow<DismissAction> =
MutableStateFlow(DismissAction.None)
@@ -468,12 +480,15 @@
awaitClose { keyguardStateController.removeCallback(callback) }
}
- private val _isKeyguardEnabled = MutableStateFlow(true)
+ private val _isKeyguardEnabled =
+ MutableStateFlow(!lockPatternUtils.isLockScreenDisabled(userTracker.userId))
override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
private val _isDozing = MutableStateFlow(statusBarStateController.isDozing)
override val isDozing: StateFlow<Boolean> = _isDozing.asStateFlow()
+ private var isShowKeyguardWhenReenabled: Boolean = false
+
override fun setIsDozing(isDozing: Boolean) {
_isDozing.value = isDozing
}
@@ -692,6 +707,16 @@
_isKeyguardEnabled.value = enabled
}
+ override fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean) {
+ SceneContainerFlag.assertInNewMode()
+ this.isShowKeyguardWhenReenabled = isShowKeyguardWhenReenabled
+ }
+
+ override fun isShowKeyguardWhenReenabled(): Boolean {
+ SceneContainerFlag.assertInNewMode()
+ return isShowKeyguardWhenReenabled
+ }
+
private fun statusBarStateIntToObject(value: Int): StatusBarState {
return when (value) {
0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
index 6522439..bd5d096 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/TrustRepository.kt
@@ -23,11 +23,13 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.model.ActiveUnlockModel
import com.android.systemui.keyguard.shared.model.TrustManagedModel
import com.android.systemui.keyguard.shared.model.TrustModel
import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -44,6 +46,7 @@
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/** Encapsulates any state relevant to trust agents and trust grants. */
interface TrustRepository {
@@ -51,7 +54,7 @@
val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean>
/** Flow representing whether the current user is trusted. */
- val isCurrentUserTrusted: Flow<Boolean>
+ val isCurrentUserTrusted: StateFlow<Boolean>
/** Flow representing whether active unlock is running for the current user. */
val isCurrentUserActiveUnlockRunning: Flow<Boolean>
@@ -63,6 +66,9 @@
/** A trust agent is requesting to dismiss the keyguard from a trust change. */
val trustAgentRequestingToDismissKeyguard: Flow<TrustModel>
+
+ /** Reports a keyguard visibility change. */
+ suspend fun reportKeyguardShowingChanged()
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -71,6 +77,7 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
private val userRepository: UserRepository,
private val trustManager: TrustManager,
private val logger: TrustRepositoryLogger,
@@ -191,11 +198,26 @@
private fun isUserTrustManaged(userId: Int) =
trustManagedForUser[userId]?.isTrustManaged ?: false
- override val isCurrentUserTrusted: Flow<Boolean>
+ override val isCurrentUserTrusted: StateFlow<Boolean>
get() =
combine(trust, userRepository.selectedUserInfo, ::Pair)
- .map { latestTrustModelForUser[it.second.id]?.isTrusted ?: false }
+ .map { isCurrentUserTrusted(it.second.id) }
.distinctUntilChanged()
.onEach { logger.isCurrentUserTrusted(it) }
.onStart { emit(false) }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = isCurrentUserTrusted(),
+ )
+
+ private fun isCurrentUserTrusted(
+ selectedUserId: Int = userRepository.getSelectedUserInfo().id
+ ): Boolean {
+ return latestTrustModelForUser[selectedUserId]?.isTrusted ?: false
+ }
+
+ override suspend fun reportKeyguardShowingChanged() {
+ withContext(backgroundDispatcher) { trustManager.reportKeyguardShowingChanged() }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 49d00af..5573f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -40,6 +40,7 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
@@ -168,7 +169,9 @@
keyguardInteractor.isKeyguardGoingAway.filter { it }.map {}, // map to Unit
keyguardInteractor.isKeyguardOccluded.flatMapLatest { keyguardOccluded ->
if (keyguardOccluded) {
- primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled
+ primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled.drop(
+ 1
+ ) // drop the initial state
} else {
emptyFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index f3692bd..506a18d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -17,8 +17,10 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
+import android.app.DreamManager
import com.android.app.animation.Interpolators
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -51,8 +53,10 @@
keyguardInteractor: KeyguardInteractor,
powerInteractor: PowerInteractor,
private val communalInteractor: CommunalInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
val deviceEntryRepository: DeviceEntryRepository,
+ private val dreamManager: DreamManager,
) :
TransitionInteractor(
fromState = KeyguardState.DOZING,
@@ -119,7 +123,8 @@
.filterRelevantKeyguardStateAnd { isAwake -> isAwake }
.sample(
keyguardInteractor.isKeyguardOccluded,
- communalInteractor.isIdleOnCommunal,
+ communalInteractor.isCommunalAvailable,
+ communalSceneInteractor.isIdleOnCommunal,
canTransitionToGoneOnWake,
keyguardInteractor.primaryBouncerShowing,
)
@@ -127,6 +132,7 @@
(
_,
occluded,
+ isCommunalAvailable,
isIdleOnCommunal,
canTransitionToGoneOnWake,
primaryBouncerShowing) ->
@@ -141,6 +147,10 @@
KeyguardState.OCCLUDED
} else if (isIdleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
+ } else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
+ // This case handles tapping the power button to transition through
+ // dream -> off -> hub.
+ KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
}
@@ -159,7 +169,8 @@
powerInteractor.detailedWakefulness
.filterRelevantKeyguardStateAnd { it.isAwake() }
.sample(
- communalInteractor.isIdleOnCommunal,
+ communalInteractor.isCommunalAvailable,
+ communalSceneInteractor.isIdleOnCommunal,
keyguardInteractor.biometricUnlockState,
canTransitionToGoneOnWake,
keyguardInteractor.primaryBouncerShowing,
@@ -167,6 +178,7 @@
.collect {
(
_,
+ isCommunalAvailable,
isIdleOnCommunal,
biometricUnlockState,
canDismissLockscreen,
@@ -188,6 +200,10 @@
KeyguardState.PRIMARY_BOUNCER
} else if (isIdleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
+ } else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
+ // This case handles tapping the power button to transition through
+ // dream -> off -> hub.
+ KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
},
@@ -216,5 +232,6 @@
val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
val TO_GONE_DURATION = DEFAULT_DURATION
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
+ val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index f5b12a2..07a2b04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -99,19 +99,21 @@
}
}
- scope.launch {
- keyguardRepository.isKeyguardEnabled
- .filterRelevantKeyguardStateAnd { enabled -> enabled }
- .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
- .filter { reshow -> reshow }
- .collect {
- startTransitionTo(
- KeyguardState.LOCKSCREEN,
- ownerReason =
- "Keyguard was re-enabled, and we weren't GONE when it " +
- "was originally disabled"
- )
- }
+ if (!SceneContainerFlag.isEnabled) {
+ scope.launch {
+ keyguardRepository.isKeyguardEnabled
+ .filterRelevantKeyguardStateAnd { enabled -> enabled }
+ .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
+ .filter { reshow -> reshow }
+ .collect {
+ startTransitionTo(
+ KeyguardState.LOCKSCREEN,
+ ownerReason =
+ "Keyguard was re-enabled, and we weren't GONE when it " +
+ "was originally disabled"
+ )
+ }
+ }
}
} else {
scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index f30eef0..35a2d58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -368,7 +369,12 @@
// being delayed in KeyguardViewMediator
KeyguardState.DREAMING -> TO_DREAMING_DURATION + 100.milliseconds
KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
- KeyguardState.AOD -> TO_AOD_DURATION
+ KeyguardState.AOD ->
+ if (powerInteractor.detailedWakefulness.value.lastSleepReason == FOLD) {
+ TO_AOD_FOLD_DURATION
+ } else {
+ TO_AOD_DURATION
+ }
KeyguardState.DOZING -> TO_DOZING_DURATION
KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION
KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
@@ -385,6 +391,7 @@
val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
val TO_OCCLUDED_DURATION = 450.milliseconds
val TO_AOD_DURATION = 500.milliseconds
+ val TO_AOD_FOLD_DURATION = 1100.milliseconds
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
val TO_GONE_DURATION = 633.milliseconds
val TO_GLANCEABLE_HUB_DURATION = 1.seconds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index f8208b3..f23b12f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -233,6 +233,7 @@
KeyguardState.DOZING -> TO_DOZING_DURATION
KeyguardState.GONE -> TO_GONE_DURATION
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+ KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -245,6 +246,7 @@
val TO_GONE_DURATION = 500.milliseconds
val TO_GONE_SHORT_DURATION = 200.milliseconds
val TO_LOCKSCREEN_DURATION = 450.milliseconds
+ val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 8dede01..8d683f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -21,12 +21,15 @@
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
/**
@@ -48,6 +51,38 @@
transitionInteractor: KeyguardTransitionInteractor,
) {
+ /**
+ * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the
+ * lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE.
+ *
+ * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold
+ * permission to do so (such as Phone).
+ *
+ * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in
+ * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were
+ * locked when it was disabled.
+ */
+ val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled
+
+ /**
+ * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it
+ * became disabled.
+ */
+ val showKeyguardWhenReenabled: Flow<Boolean> =
+ repository.isKeyguardEnabled
+ .onEach { SceneContainerFlag.assertInLegacyMode() }
+ // Whenever the keyguard is disabled...
+ .filter { enabled -> !enabled }
+ .sampleCombine(
+ transitionInteractor.currentTransitionInfoInternal,
+ biometricSettingsRepository.isCurrentUserInLockdown
+ )
+ .map { (_, transitionInfo, inLockdown) ->
+ // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
+ // we want to remember that and re-show it when keyguard is enabled again.
+ transitionInfo.to != KeyguardState.GONE && !inLockdown
+ }
+
init {
/**
* Whenever keyguard is disabled, transition to GONE unless we're in lockdown or already
@@ -68,25 +103,15 @@
}
}
- /**
- * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it
- * became disabled.
- */
- val showKeyguardWhenReenabled: Flow<Boolean> =
- repository.isKeyguardEnabled
- // Whenever the keyguard is disabled...
- .filter { enabled -> !enabled }
- .sampleCombine(
- transitionInteractor.currentTransitionInfoInternal,
- biometricSettingsRepository.isCurrentUserInLockdown
- )
- .map { (_, transitionInfo, inLockdown) ->
- // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
- // we want to remember that and re-show it when keyguard is enabled again.
- transitionInfo.to != KeyguardState.GONE && !inLockdown
- }
-
fun notifyKeyguardEnabled(enabled: Boolean) {
repository.setKeyguardEnabled(enabled)
}
+
+ fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean) {
+ repository.setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled)
+ }
+
+ fun isShowKeyguardWhenReenabled(): Boolean {
+ return repository.isShowKeyguardWhenReenabled()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
index bb6215a..7a06d2f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.res.R
+import com.android.systemui.shade.PulsingGestureListener
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -51,10 +52,10 @@
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-/** Business logic for use-cases related to the keyguard long-press feature. */
+/** Business logic for use-cases related to top-level touch handling in the lock screen. */
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
-class KeyguardLongPressInteractor
+class KeyguardTouchHandlingInteractor
@Inject
constructor(
@Application private val appContext: Context,
@@ -65,6 +66,7 @@
private val featureFlags: FeatureFlags,
broadcastDispatcher: BroadcastDispatcher,
private val accessibilityManager: AccessibilityManagerWrapper,
+ private val pulsingGestureListener: PulsingGestureListener,
) {
/** Whether the long-press handling feature should be enabled. */
val isLongPressHandlingEnabled: StateFlow<Boolean> =
@@ -166,6 +168,16 @@
_shouldOpenSettings.value = false
}
+ /** Notifies that the lockscreen has been clicked at position [x], [y]. */
+ fun onClick(x: Float, y: Float) {
+ pulsingGestureListener.onSingleTapUp(x, y)
+ }
+
+ /** Notifies that the lockscreen has been double clicked. */
+ fun onDoubleClick() {
+ pulsingGestureListener.onDoubleTapEvent()
+ }
+
private fun showSettings() {
_shouldOpenSettings.value = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
index 6729246..21b9e53 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
@@ -16,30 +16,17 @@
package com.android.systemui.keyguard.domain.interactor
-import android.animation.ValueAnimator
import android.view.ViewGroup
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.shade.ShadeFoldAnimator
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
@SysUISingleton
class ToAodFoldTransitionInteractor
@Inject
constructor(
private val keyguardClockInteractor: KeyguardClockInteractor,
- private val transitionInteractor: KeyguardTransitionInteractor,
- private val transitionRepository: KeyguardTransitionRepository,
- @Application private val mainScope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
) {
private var parentAnimator: NotificationPanelViewController.ShadeFoldAnimatorImpl? = null
@@ -50,7 +37,6 @@
get() = throw NotImplementedError("Deprecated. Do not call.")
override fun prepareFoldToAodAnimation() {
- forceToAod()
parentAnimator?.prepareFoldToAodAnimation()
}
@@ -78,21 +64,6 @@
parentAnimator as? NotificationPanelViewController.ShadeFoldAnimatorImpl?
}
- /** Forces the keyguard into AOD or Doze */
- private fun forceToAod() {
- mainScope.launch(mainDispatcher) {
- transitionRepository.startTransition(
- TransitionInfo(
- "$TAG (Fold transition triggered)",
- transitionInteractor.getCurrentState(),
- transitionInteractor.asleepKeyguardState.value,
- ValueAnimator().apply { duration = 0 },
- TransitionModeOnCanceled.LAST_VALUE,
- )
- )
- }
- }
-
companion object {
private val TAG = ToAodFoldTransitionInteractor::class.simpleName!!
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt
index 2ff6e16..73248bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt
@@ -17,14 +17,21 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.TrustRepository
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
/** Encapsulates any state relevant to trust agents and trust grants. */
@SysUISingleton
-class TrustInteractor @Inject constructor(repository: TrustRepository) {
+class TrustInteractor
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val repository: TrustRepository,
+) {
/**
* Whether the current user has a trust agent enabled. This is true if the user has at least one
* trust agent enabled in settings.
@@ -39,5 +46,10 @@
val isTrustAgentCurrentlyAllowed: StateFlow<Boolean> = repository.isCurrentUserTrustManaged
/** Whether the current user is trusted by any of the enabled trust agents. */
- val isTrusted: Flow<Boolean> = repository.isCurrentUserTrusted
+ val isTrusted: StateFlow<Boolean> = repository.isCurrentUserTrusted
+
+ /** Reports a keyguard visibility change. */
+ fun reportKeyguardShowingChanged() {
+ applicationScope.launch { repository.reportKeyguardShowingChanged() }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 6550937..db33acb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -86,7 +86,10 @@
privateFlags =
WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY or
WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ // Avoid announcing window title.
+ accessibilityTitle = " "
}
+
private var alternateBouncerView: ConstraintLayout? = null
override fun start() {
@@ -112,13 +115,28 @@
}
private fun removeViewFromWindowManager() {
- if (alternateBouncerView == null || !alternateBouncerView!!.isAttachedToWindow) {
- return
- }
+ alternateBouncerView?.let {
+ alternateBouncerView = null
+ if (it.isAttachedToWindow) {
+ it.removeOnAttachStateChangeListener(onAttachAddBackGestureHandler)
+ Log.d(TAG, "Removing alternate bouncer view immediately")
+ windowManager.get().removeView(it)
+ } else {
+ // once the view is attached, remove it
+ it.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(view: View) {
+ it.removeOnAttachStateChangeListener(this)
+ it.removeOnAttachStateChangeListener(onAttachAddBackGestureHandler)
+ Log.d(TAG, "Removing alternate bouncer view on attached")
+ windowManager.get().removeView(it)
+ }
- windowManager.get().removeView(alternateBouncerView)
- alternateBouncerView!!.removeOnAttachStateChangeListener(onAttachAddBackGestureHandler)
- alternateBouncerView = null
+ override fun onViewDetachedFromWindow(view: View) {}
+ }
+ )
+ }
+ }
}
private val onAttachAddBackGestureHandler =
@@ -148,7 +166,7 @@
}
private fun addViewToWindowManager() {
- if (alternateBouncerView?.isAttachedToWindow == true) {
+ if (alternateBouncerView != null) {
return
}
@@ -156,6 +174,7 @@
layoutInflater.get().inflate(R.layout.alternate_bouncer, null, false)
as ConstraintLayout
+ Log.d(TAG, "Adding alternate bouncer view")
windowManager.get().addView(alternateBouncerView, layoutParams)
alternateBouncerView!!.addOnAttachStateChangeListener(onAttachAddBackGestureHandler)
}
@@ -304,6 +323,7 @@
}
}
}
+
companion object {
private const val TAG = "AlternateBouncerViewBinder"
private const val swipeTag = "AlternateBouncer-SWIPE"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 23c2491..ba94f45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -29,6 +29,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.util.kotlin.DisposableHandles
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,7 +54,15 @@
viewModel: KeyguardIndicationAreaViewModel,
indicationController: KeyguardIndicationController,
): DisposableHandle {
- indicationController.setIndicationArea(view)
+ val disposables = DisposableHandles()
+
+ // As the indication controller is a singleton, reset the view back to the previous view
+ // once the current view is disposed.
+ val previous = indicationController.indicationArea
+ indicationController.indicationArea = view
+ disposables += DisposableHandle {
+ previous?.let { indicationController.indicationArea = it }
+ }
val indicationText: TextView = view.requireViewById(R.id.keyguard_indication_text)
val indicationTextBottom: TextView =
@@ -63,7 +72,7 @@
view.clipToPadding = false
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
- val disposableHandle =
+ disposables +=
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch("$TAG#viewModel.alpha") {
@@ -124,9 +133,15 @@
configurationBasedDimensions.value = loadFromResources(view)
}
}
+
+ launch("$TAG#viewModel.visible") {
+ viewModel.visible.collect { visible ->
+ indicationController.setVisible(visible)
+ }
+ }
}
}
- return disposableHandle
+ return disposables
}
private fun loadFromResources(view: View): ConfigurationBasedDimensions {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
index 09fe067..057b4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
@@ -22,7 +22,7 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launch
import com.android.systemui.common.ui.view.LongPressHandlingView
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
@@ -39,7 +39,7 @@
@JvmStatic
fun bind(
view: LongPressHandlingView,
- viewModel: KeyguardLongPressViewModel,
+ viewModel: KeyguardTouchHandlingViewModel,
onSingleTap: () -> Unit,
falsingManager: FalsingManager,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index fa57565..4150ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -26,9 +26,9 @@
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils
import com.android.systemui.keyguard.util.WallpaperPickerIntentUtils.LAUNCH_SOURCE_KEYGUARD
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -44,7 +44,7 @@
fun bind(
view: View,
viewModel: KeyguardSettingsMenuViewModel,
- longPressViewModel: KeyguardLongPressViewModel,
+ touchHandlingViewModel: KeyguardTouchHandlingViewModel,
rootViewModel: KeyguardRootViewModel?,
vibratorHelper: VibratorHelper,
activityStarter: ActivityStarter
@@ -97,7 +97,7 @@
val hitRect = Rect()
view.getHitRect(hitRect)
if (!hitRect.contains(point.x, point.y)) {
- longPressViewModel.onTouchedOutside()
+ touchHandlingViewModel.onTouchedOutside()
}
}
}
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 777c873..4f0ac42 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
@@ -197,8 +197,7 @@
initiallySelectedSlotId =
bundle.getString(
KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
- )
- ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+ ) ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
} else {
@@ -230,8 +229,7 @@
val previewContext =
display?.let {
ContextThemeWrapper(context.createDisplayContext(it), context.getTheme())
- }
- ?: context
+ } ?: context
val rootView = FrameLayout(previewContext)
@@ -318,8 +316,8 @@
*/
private fun setUpSmartspace(previewContext: Context, parentView: ViewGroup) {
if (
- !lockscreenSmartspaceController.isEnabled() ||
- !lockscreenSmartspaceController.isDateWeatherDecoupled()
+ !lockscreenSmartspaceController.isEnabled ||
+ !lockscreenSmartspaceController.isDateWeatherDecoupled
) {
return
}
@@ -654,6 +652,7 @@
clockController.clock = clock
}
}
+
private fun onClockChanged() {
if (MigrateClocksToBlueprint.isEnabled) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index ecdc21c..dc7a649 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel
@@ -49,6 +50,7 @@
import com.android.systemui.keyguard.ui.viewmodel.OffToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToDozingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import dagger.Binds
import dagger.Module
@@ -253,4 +255,16 @@
abstract fun goneToGlanceableHub(
impl: GoneToGlanceableHubTransitionViewModel
): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun primaryBouncerToGlanceableHub(
+ impl: PrimaryBouncerToGlanceableHubTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
+ abstract fun dozingToGlanceableHub(
+ impl: DozingToGlanceableHubTransitionViewModel
+ ): DeviceEntryIconTransition
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 215ac46..1c63235 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -73,14 +73,14 @@
AccessibilityNodeInfoCompat.ACTION_CLICK,
resources.getString(R.string.accessibility_enter_hint)
)
+
override fun onInitializeAccessibilityNodeInfo(
v: View,
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(v, info)
when (accessibilityHintType) {
- AccessibilityHintType.BOUNCER ->
- info.addAction(accessibilityBouncerHint)
+ AccessibilityHintType.BOUNCER -> info.addAction(accessibilityBouncerHint)
AccessibilityHintType.ENTER -> info.addAction(accessibilityEnterHint)
AccessibilityHintType.NONE -> return
}
@@ -204,12 +204,12 @@
/* reversible */ false,
)
- // LockscreenFingerprint <=> LockscreenLocked
+ // LockscreenFingerprint => LockscreenLocked
animatedIconDrawable.addTransition(
R.id.locked_fp,
R.id.locked,
context.getDrawable(R.drawable.fp_to_locked) as AnimatedVectorDrawable,
- /* reversible */ true,
+ /* reversible */ false,
)
// LockscreenUnlocked <=> AodLocked
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
index 32e76d0..5cd5172 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -34,9 +34,9 @@
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
@@ -48,7 +48,7 @@
constructor(
@Main private val resources: Resources,
private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
- private val keyguardLongPressViewModel: KeyguardLongPressViewModel,
+ private val keyguardTouchHandlingViewModel: KeyguardTouchHandlingViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
private val vibratorHelper: VibratorHelper,
private val activityStarter: ActivityStarter,
@@ -76,7 +76,7 @@
KeyguardSettingsViewBinder.bind(
constraintLayout.requireViewById<View>(R.id.keyguard_settings_button),
keyguardSettingsMenuViewModel,
- keyguardLongPressViewModel,
+ keyguardTouchHandlingViewModel,
keyguardRootViewModel,
vibratorHelper,
activityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
index a17c5e5..b33d552 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
@@ -35,7 +35,7 @@
) : KeyguardSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) return
- if (smartspaceController.isEnabled()) return
+ if (smartspaceController.isEnabled) return
constraintLayout.findViewById<View?>(R.id.keyguard_slice_view)?.let {
(it.parent as ViewGroup).removeView(it)
@@ -47,7 +47,7 @@
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!MigrateClocksToBlueprint.isEnabled) return
- if (smartspaceController.isEnabled()) return
+ if (smartspaceController.isEnabled) return
constraintSet.apply {
connect(
@@ -82,7 +82,7 @@
override fun removeViews(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) return
- if (smartspaceController.isEnabled()) return
+ if (smartspaceController.isEnabled) return
constraintLayout.removeView(R.id.keyguard_slice_view)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index d9a6d64..62b4782 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -55,6 +55,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+ private val lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
private val keyguardClockViewModel: KeyguardClockViewModel,
@@ -74,13 +75,30 @@
burnInParams
}
return configurationInteractor
- .dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y)
- .flatMapLatest { enterFromTopAmount ->
+ .dimensionPixelSize(
+ setOf(
+ R.dimen.keyguard_enter_from_top_translation_y,
+ R.dimen.keyguard_enter_from_side_translation_x,
+ )
+ )
+ .flatMapLatest { dimens ->
combine(
keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
burnIn(params).onStart { emit(BurnInModel()) },
goneToAodTransitionViewModel
- .enterFromTopTranslationY(enterFromTopAmount)
+ .enterFromTopTranslationY(
+ dimens[R.dimen.keyguard_enter_from_top_translation_y]!!
+ )
+ .onStart { emit(StateToValue()) },
+ goneToAodTransitionViewModel
+ .enterFromSideTranslationX(
+ dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
+ )
+ .onStart { emit(StateToValue()) },
+ lockscreenToAodTransitionViewModel
+ .enterFromSideTranslationX(
+ dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
+ )
.onStart { emit(StateToValue()) },
occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
emit(0f)
@@ -88,21 +106,31 @@
aodToLockscreenTransitionViewModel.translationY(params.translationY).onStart {
emit(StateToValue())
},
- ) {
- keyguardTranslationY,
- burnInModel,
- goneToAod,
- occludedToLockscreen,
- aodToLockscreen ->
+ ) { flows ->
+ val keyguardTranslationY = flows[0] as Float
+ val burnInModel = flows[1] as BurnInModel
+ val goneToAodTranslationY = flows[2] as StateToValue
+ val goneToAodTranslationX = flows[3] as StateToValue
+ val lockscreenToAodTranslationX = flows[4] as StateToValue
+ val occludedToLockscreen = flows[5] as Float
+ val aodToLockscreen = flows[6] as StateToValue
+
val translationY =
if (aodToLockscreen.transitionState.isTransitioning()) {
aodToLockscreen.value ?: 0f
- } else if (goneToAod.transitionState.isTransitioning()) {
- (goneToAod.value ?: 0f) + burnInModel.translationY
+ } else if (goneToAodTranslationY.transitionState.isTransitioning()) {
+ (goneToAodTranslationY.value ?: 0f) + burnInModel.translationY
} else {
burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
}
- burnInModel.copy(translationY = translationY.toInt())
+ val translationX =
+ burnInModel.translationX +
+ (goneToAodTranslationX.value ?: 0f) +
+ (lockscreenToAodTranslationX.value ?: 0f)
+ burnInModel.copy(
+ translationX = translationX.toInt(),
+ translationY = translationY.toInt(),
+ )
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 4688088..5ce1b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -284,9 +284,9 @@
private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
DeviceEntryIconView.AccessibilityHintType {
return when (this) {
+ DeviceEntryIconView.IconType.FINGERPRINT,
DeviceEntryIconView.IconType.LOCK -> DeviceEntryIconView.AccessibilityHintType.BOUNCER
DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
- DeviceEntryIconView.IconType.FINGERPRINT,
DeviceEntryIconView.IconType.NONE -> DeviceEntryIconView.AccessibilityHintType.NONE
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.kt
new file mode 100644
index 0000000..aee34e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGlanceableHubTransitionViewModel.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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class DozingToGlanceableHubTransitionViewModel
+@Inject
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(DOZING, Scenes.Communal)
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(DOZING, GLANCEABLE_HUB))
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 74f7d75..2bc8e51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -26,13 +26,17 @@
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.transform
/** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
@ExperimentalCoroutinesApi
@@ -41,6 +45,7 @@
@Inject
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ private val powerInteractor: PowerInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -56,13 +61,38 @@
/** y-translation from the top of the screen for AOD */
fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> {
- return transitionAnimation.sharedFlowWithState(
- startTime = 600.milliseconds,
- duration = 500.milliseconds,
- onStep = { translatePx + it * -translatePx },
- onFinish = { 0f },
- interpolator = EMPHASIZED_DECELERATE,
- )
+ return transitionAnimation
+ .sharedFlowWithState(
+ startTime = 600.milliseconds,
+ duration = 500.milliseconds,
+ onStep = { translatePx + it * -translatePx },
+ onFinish = { 0f },
+ interpolator = EMPHASIZED_DECELERATE,
+ )
+ .sample(powerInteractor.detailedWakefulness, ::Pair)
+ .transform { (stateToValue, wakefulness) ->
+ if (wakefulness.lastSleepReason != FOLD) {
+ emit(stateToValue)
+ }
+ }
+ }
+
+ /** x-translation from the side of the screen for fold animation */
+ fun enterFromSideTranslationX(translatePx: Int): Flow<StateToValue> {
+ return transitionAnimation
+ .sharedFlowWithState(
+ startTime = 500.milliseconds,
+ duration = 600.milliseconds,
+ onStep = { translatePx + it * -translatePx },
+ onFinish = { 0f },
+ interpolator = EMPHASIZED_DECELERATE,
+ )
+ .sample(powerInteractor.detailedWakefulness, ::Pair)
+ .transform { (stateToValue, wakefulness) ->
+ if (wakefulness.lastSleepReason == FOLD) {
+ emit(stateToValue)
+ }
+ }
}
val notificationAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 3e6f8e6..6fe51ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -44,7 +44,7 @@
private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
private val burnInHelperWrapper: BurnInHelperWrapper,
- private val longPressViewModel: KeyguardLongPressViewModel,
+ private val keyguardTouchHandlingViewModel: KeyguardTouchHandlingViewModel,
val settingsMenuViewModel: KeyguardSettingsMenuViewModel,
) {
data class PreviewMode(
@@ -162,7 +162,7 @@
* the lock screen settings menu item pop-up.
*/
fun onTouchedOutsideLockScreenSettingsMenu() {
- longPressViewModel.onTouchedOutside()
+ keyguardTouchHandlingViewModel.onTouchedOutside()
}
private fun button(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index a758720d..609b571d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.doze.util.BurnInHelperWrapper
@@ -28,9 +29,10 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -44,14 +46,15 @@
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
- private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
+ bottomAreaInteractor: KeyguardBottomAreaInteractor,
keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
private val burnInHelperWrapper: BurnInHelperWrapper,
- private val burnInInteractor: BurnInInteractor,
- private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
+ burnInInteractor: BurnInInteractor,
+ shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
configurationInteractor: ConfigurationInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
- @Background private val backgroundCoroutineContext: CoroutineContext,
+ communalSceneInteractor: CommunalSceneInteractor,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
@Main private val mainDispatcher: CoroutineDispatcher,
) {
@@ -61,6 +64,13 @@
/** An observable for the alpha level for the entire bottom area. */
val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha
+ /** An observable for the visibility value for the indication area view. */
+ val visible: Flow<Boolean> =
+ anyOf(
+ keyguardInteractor.statusBarState.map { state -> state == StatusBarState.KEYGUARD },
+ communalSceneInteractor.isCommunalVisible
+ )
+
/** An observable for whether the indication area should be padded. */
val isIndicationAreaPadded: Flow<Boolean> =
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -97,7 +107,7 @@
)
}
.distinctUntilChanged()
- .flowOn(backgroundCoroutineContext)
+ .flowOn(backgroundDispatcher)
/** An observable for the x-offset by which the indication area should be translated. */
val indicationAreaTranslationX: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index aefff7d..d7ac976 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -251,6 +251,7 @@
goneToDreamingTransitionViewModel.lockscreenAlpha,
goneToLockscreenTransitionViewModel.lockscreenAlpha,
lockscreenToAodTransitionViewModel.lockscreenAlpha(viewState),
+ lockscreenToAodTransitionViewModel.lockscreenAlphaOnFold,
lockscreenToDozingTransitionViewModel.lockscreenAlpha,
lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
index 66ceded..36a342b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSettingsMenuViewModel.kt
@@ -17,10 +17,10 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.res.R
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
-import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTouchHandlingInteractor
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -28,7 +28,7 @@
class KeyguardSettingsMenuViewModel
@Inject
constructor(
- private val interactor: KeyguardLongPressInteractor,
+ private val interactor: KeyguardTouchHandlingInteractor,
) {
val isVisible: Flow<Boolean> = interactor.isMenuVisible
val shouldOpenSettings: Flow<Boolean> = interactor.shouldOpenSettings
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
index c0b1f95..e30ddc6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt
@@ -40,12 +40,12 @@
smartspaceInteractor: KeyguardSmartspaceInteractor,
) {
/** Whether the smartspace section is available in the build. */
- val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled()
+ val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled
/** Whether the weather area is available in the build. */
private val isWeatherEnabled: StateFlow<Boolean> = smartspaceInteractor.isWeatherEnabled
/** Whether the data and weather areas are decoupled in the build. */
- val isDateWeatherDecoupled: Boolean = smartspaceController.isDateWeatherDecoupled()
+ val isDateWeatherDecoupled: Boolean = smartspaceController.isDateWeatherDecoupled
/** Whether the date area should be visible. */
val isDateVisible: StateFlow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardTouchHandlingViewModel.kt
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardTouchHandlingViewModel.kt
index c73931a..f1cbf25 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardTouchHandlingViewModel.kt
@@ -18,16 +18,16 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTouchHandlingInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-/** Models UI state to support the lock screen long-press feature. */
+/** Models UI state to support top-level touch handling in the lock screen. */
@SysUISingleton
-class KeyguardLongPressViewModel
+class KeyguardTouchHandlingViewModel
@Inject
constructor(
- private val interactor: KeyguardLongPressInteractor,
+ private val interactor: KeyguardTouchHandlingInteractor,
) {
/** Whether the long-press handling feature should be enabled. */
@@ -45,4 +45,14 @@
fun onTouchedOutside() {
interactor.onTouchedOutside()
}
+
+ /** Notifies that the lockscreen has been clicked at position [x], [y]. */
+ fun onClick(x: Float, y: Float) {
+ interactor.onClick(x, y)
+ }
+
+ /** Notifies that the lockscreen has been double clicked. */
+ fun onDoubleClick() {
+ interactor.onDoubleClick()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index b33eaa2..1de0abe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -44,7 +44,7 @@
clockInteractor: KeyguardClockInteractor,
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
- val longPress: KeyguardLongPressViewModel,
+ val touchHandling: KeyguardTouchHandlingViewModel,
val shadeInteractor: ShadeInteractor,
@Application private val applicationScope: CoroutineScope,
unfoldTransitionInteractor: UnfoldTransitionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 10cfd6b..630dcca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -53,7 +53,7 @@
deviceEntryInteractor: DeviceEntryInteractor,
communalInteractor: CommunalInteractor,
shadeInteractor: ShadeInteractor,
- val longPress: KeyguardLongPressViewModel,
+ val touchHandling: KeyguardTouchHandlingViewModel,
val notifications: NotificationsPlaceholderViewModel,
) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 8b5b347..5408428 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
+import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
@@ -24,12 +25,17 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.transform
/**
* Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume.
@@ -40,6 +46,7 @@
@Inject
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ private val powerInteractor: PowerInteractor,
shadeDependentFlows: ShadeDependentFlows,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -50,6 +57,12 @@
edge = Edge.create(from = LOCKSCREEN, to = AOD),
)
+ private val transitionAnimationOnFold =
+ animationFlow.setup(
+ duration = FromLockscreenTransitionInteractor.TO_AOD_FOLD_DURATION,
+ edge = Edge.create(from = LOCKSCREEN, to = AOD),
+ )
+
val deviceEntryBackgroundViewAlpha: Flow<Float> =
shadeDependentFlows.transitionFlow(
flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
@@ -71,11 +84,64 @@
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
var startAlpha = 1f
- return transitionAnimation.sharedFlow(
- duration = 500.milliseconds,
- onStart = { startAlpha = viewState.alpha() },
- onStep = { MathUtils.lerp(startAlpha, 1f, it) },
- )
+ return transitionAnimation
+ .sharedFlow(
+ duration = 500.milliseconds,
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 1f, it) },
+ )
+ .sample(powerInteractor.detailedWakefulness, ::Pair)
+ .transform { (alpha, wakefulness) ->
+ if (wakefulness.lastSleepReason != FOLD) {
+ emit(alpha)
+ }
+ }
+ }
+
+ val lockscreenAlphaOnFold: Flow<Float> =
+ transitionAnimationOnFold
+ .sharedFlow(
+ startTime = 600.milliseconds,
+ duration = 500.milliseconds,
+ onStep = { it },
+ )
+ .sample(powerInteractor.detailedWakefulness, ::Pair)
+ .transform { (alpha, wakefulness) ->
+ if (wakefulness.lastSleepReason == FOLD) {
+ emit(alpha)
+ }
+ }
+
+ val notificationAlphaOnFold: Flow<Float> =
+ transitionAnimationOnFold
+ .sharedFlow(
+ duration = 1100.milliseconds,
+ onStep = { 0f },
+ onFinish = { 1f },
+ )
+ .sample(powerInteractor.detailedWakefulness, ::Pair)
+ .transform { (alpha, wakefulness) ->
+ if (wakefulness.lastSleepReason == FOLD) {
+ emit(alpha)
+ }
+ }
+
+ /** x-translation from the side of the screen for fold animation */
+ fun enterFromSideTranslationX(translatePx: Int): Flow<StateToValue> {
+ return transitionAnimationOnFold
+ .sharedFlowWithState(
+ startTime = 600.milliseconds,
+ duration = 500.milliseconds,
+ onStep = { translatePx + it * -translatePx },
+ onFinish = { 0f },
+ interpolator = EMPHASIZED_DECELERATE,
+ )
+ .sample(powerInteractor.detailedWakefulness, ::Pair)
+ .transform { (stateToValue, wakefulness) ->
+ if (wakefulness.lastSleepReason == FOLD) {
+ emit(stateToValue)
+ }
+ }
}
override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
new file mode 100644
index 0000000..754fb94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class PrimaryBouncerToGlanceableHubTransitionViewModel
+@Inject
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
+ private val transitionAnimation =
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(PRIMARY_BOUNCER, Scenes.Communal)
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(PRIMARY_BOUNCER, GLANCEABLE_HUB))
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index c7fde48..52b0b87 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -674,4 +674,11 @@
return factory.create("DeviceEntryIconLog", 100);
}
+ /** Provides a {@link LogBuffer} for use by the volume loggers. */
+ @Provides
+ @SysUISingleton
+ @VolumeLog
+ public static LogBuffer provideVolumeLogBuffer(LogBufferFactory factory) {
+ return factory.create("VolumeLog", 50);
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/VolumeLog.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/log/dagger/VolumeLog.kt
index d8af3fa..bc3858a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/VolumeLog.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.log.dagger
-import com.android.systemui.kosmos.Kosmos
+import javax.inject.Qualifier
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+/** A [com.android.systemui.log.LogBuffer] for volume. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class VolumeLog
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index 6a91d1b..341b8d8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -26,6 +26,10 @@
import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_SEEN_EVENT
+import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.time.SystemClock
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
@@ -43,9 +47,10 @@
class MediaFilterRepository
@Inject
constructor(
- @Application applicationContext: Context,
+ @Application private val applicationContext: Context,
private val systemClock: SystemClock,
private val configurationController: ConfigurationController,
+ private val smartspaceLogger: MediaSmartspaceLogger,
) {
val onAnyMediaConfigurationChange: Flow<Unit> = conflatedCallbackFlow {
@@ -133,10 +138,13 @@
return mediaData
}
- fun addSelectedUserMediaEntry(data: MediaData) {
+ /** @return whether the added media data already exists. */
+ fun addSelectedUserMediaEntry(data: MediaData): Boolean {
val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
+ val update = _selectedUserEntries.value.containsKey(data.instanceId)
entries[data.instanceId] = data
_selectedUserEntries.value = entries
+ return update
}
/**
@@ -180,7 +188,10 @@
_reactivatedId.value = instanceId
}
- fun addMediaDataLoadingState(mediaDataLoadingModel: MediaDataLoadingModel) {
+ fun addMediaDataLoadingState(
+ mediaDataLoadingModel: MediaDataLoadingModel,
+ isUpdate: Boolean = true
+ ) {
val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
sortedMap.putAll(
sortedMedia.filter { (_, commonModel) ->
@@ -208,7 +219,8 @@
MediaCommonModel.MediaControl(
mediaDataLoadingModel,
canBeRemoved(it),
- isMediaFromRec(it)
+ isMediaFromRec(it),
+ if (isUpdate) systemClock.currentTimeMillis() else 0,
)
sortedMap[sortKey] = newCommonModel
@@ -218,30 +230,50 @@
mediaFromRecPackageName = null
_currentMedia.value = sortedMap.values.toList()
}
- } else if (sortedMap.size > _currentMedia.value.size && it.active) {
- _currentMedia.value = sortedMap.values.toList()
} else {
- // When loading an update for an existing media control.
+ var isNewToCurrentMedia = true
val currentList =
mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) }
currentList.forEachIndexed { index, mediaCommonModel ->
if (
mediaCommonModel is MediaCommonModel.MediaControl &&
mediaCommonModel.mediaLoadedModel.instanceId ==
- mediaDataLoadingModel.instanceId &&
- mediaCommonModel != newCommonModel
+ mediaDataLoadingModel.instanceId
) {
- // Update media model if changed.
- currentList[index] = newCommonModel
+ // When loading an update for an existing media control.
+ isNewToCurrentMedia = false
+ if (mediaCommonModel != newCommonModel) {
+ // Update media model if changed.
+ currentList[index] = newCommonModel
+ }
}
}
- _currentMedia.value = currentList
+ if (isNewToCurrentMedia && it.active) {
+ _currentMedia.value = sortedMap.values.toList()
+ } else {
+ _currentMedia.value = currentList
+ }
+ }
+
+ sortedMedia = sortedMap
+
+ if (!isUpdate) {
+ val rank = sortedMedia.values.indexOf(newCommonModel)
+ if (isSmartspaceLoggingEnabled(newCommonModel, rank)) {
+ smartspaceLogger.logSmartspaceCardReceived(
+ it.smartspaceId,
+ it.appUid,
+ cardinality = _currentMedia.value.size,
+ isSsReactivated = mediaDataLoadingModel.isSsReactivated,
+ rank = rank,
+ )
+ }
+ } else if (mediaDataLoadingModel.receivedSmartspaceCardLatency != 0) {
+ logSmartspaceAllMediaCards(mediaDataLoadingModel.receivedSmartspaceCardLatency)
}
}
}
- sortedMedia = sortedMap
-
// On removal we want to keep the order being shown to user.
if (mediaDataLoadingModel is MediaDataLoadingModel.Removed) {
_currentMedia.value =
@@ -249,6 +281,7 @@
commonModel !is MediaCommonModel.MediaControl ||
mediaDataLoadingModel.instanceId != commonModel.mediaLoadedModel.instanceId
}
+ sortedMedia = sortedMap
}
}
@@ -271,21 +304,45 @@
isPlaying = false,
active = _smartspaceMediaData.value.isActive,
)
+ val newCommonModel = MediaCommonModel.MediaRecommendations(smartspaceMediaLoadingModel)
when (smartspaceMediaLoadingModel) {
- is SmartspaceMediaLoadingModel.Loaded ->
- sortedMap[sortKey] =
- MediaCommonModel.MediaRecommendations(smartspaceMediaLoadingModel)
- is SmartspaceMediaLoadingModel.Removed ->
+ is SmartspaceMediaLoadingModel.Loaded -> {
+ sortedMap[sortKey] = newCommonModel
+ _currentMedia.value = sortedMap.values.toList()
+ sortedMedia = sortedMap
+
+ if (isRecommendationActive()) {
+ val hasActivatedExistedResumeMedia =
+ !hasActiveMedia() &&
+ hasAnyMedia() &&
+ smartspaceMediaLoadingModel.isPrioritized
+ if (hasActivatedExistedResumeMedia) {
+ // Log resume card received if resumable media card is reactivated and
+ // recommendation card is valid and ranked first
+ logSmartspaceAllMediaCards(
+ (systemClock.currentTimeMillis() -
+ _smartspaceMediaData.value.headphoneConnectionTimeMillis)
+ .toInt()
+ )
+ }
+
+ smartspaceLogger.logSmartspaceCardReceived(
+ SmallHash.hash(_smartspaceMediaData.value.targetId),
+ _smartspaceMediaData.value.getUid(applicationContext),
+ cardinality = _currentMedia.value.size,
+ isRecommendationCard = true,
+ rank = _currentMedia.value.indexOf(newCommonModel),
+ )
+ }
+ }
+ is SmartspaceMediaLoadingModel.Removed -> {
_currentMedia.value =
_currentMedia.value.filter { commonModel ->
commonModel !is MediaCommonModel.MediaRecommendations
}
+ sortedMedia = sortedMap
+ }
}
-
- if (sortedMap.size > sortedMedia.size) {
- _currentMedia.value = sortedMap.values.toList()
- }
- sortedMedia = sortedMap
}
fun setOrderedMedia() {
@@ -304,10 +361,118 @@
return _selectedUserEntries.value.entries.isNotEmpty()
}
+ fun hasActiveMediaOrRecommendation(): Boolean {
+ return _selectedUserEntries.value.any { it.value.active } ||
+ (isRecommendationActive() &&
+ (_smartspaceMediaData.value.isValid() || _reactivatedId.value != null))
+ }
+
fun isRecommendationActive(): Boolean {
return _smartspaceMediaData.value.isActive
}
+ /** Log visible card given [visibleIndex]. */
+ fun logSmartspaceCardSeen(surface: Int, visibleIndex: Int, isMediaCardUpdate: Boolean) {
+ if (_currentMedia.value.size <= visibleIndex) return
+
+ when (val mediaCommonModel = _currentMedia.value[visibleIndex]) {
+ is MediaCommonModel.MediaControl -> {
+ if (
+ !isMediaCardUpdate ||
+ mediaCommonModel.mediaLoadedModel.receivedSmartspaceCardLatency != 0
+ ) {
+ logSmartspaceMediaCardUserEvent(
+ mediaCommonModel.mediaLoadedModel.instanceId,
+ visibleIndex,
+ SMARTSPACE_CARD_SEEN_EVENT,
+ surface,
+ mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ )
+ }
+ }
+ is MediaCommonModel.MediaRecommendations -> {
+ if (isRecommendationActive()) {
+ logSmarspaceRecommendationCardUserEvent(
+ SMARTSPACE_CARD_SEEN_EVENT,
+ surface,
+ visibleIndex
+ )
+ }
+ }
+ }
+ }
+
+ /** Log user event on media card if smartspace logging is enabled. */
+ fun logSmartspaceCardUserEvent(
+ eventId: Int,
+ surface: Int,
+ interactedSubCardRank: Int = 0,
+ interactedSubCardCardinality: Int = 0,
+ instanceId: InstanceId? = null,
+ isRec: Boolean = false
+ ) {
+ _currentMedia.value.forEachIndexed { index, mediaCommonModel ->
+ when (mediaCommonModel) {
+ is MediaCommonModel.MediaControl -> {
+ if (mediaCommonModel.mediaLoadedModel.instanceId == instanceId) {
+ if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
+ logSmartspaceMediaCardUserEvent(
+ instanceId,
+ index,
+ eventId,
+ surface,
+ mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ interactedSubCardRank,
+ interactedSubCardCardinality
+ )
+ }
+ return
+ }
+ }
+ is MediaCommonModel.MediaRecommendations -> {
+ if (isRec) {
+ if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
+ logSmarspaceRecommendationCardUserEvent(
+ eventId,
+ surface,
+ index,
+ interactedSubCardRank,
+ interactedSubCardCardinality
+ )
+ }
+ return
+ }
+ }
+ }
+ }
+ }
+
+ /** Log media and recommendation cards dismissal if smartspace logging is enabled for each. */
+ fun logSmartspaceCardsOnSwipeToDismiss(surface: Int) {
+ _currentMedia.value.forEachIndexed { index, mediaCommonModel ->
+ if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
+ when (mediaCommonModel) {
+ is MediaCommonModel.MediaControl ->
+ logSmartspaceMediaCardUserEvent(
+ mediaCommonModel.mediaLoadedModel.instanceId,
+ index,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ surface,
+ mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ isSwipeToDismiss = true
+ )
+ is MediaCommonModel.MediaRecommendations ->
+ logSmarspaceRecommendationCardUserEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ surface,
+ index,
+ isSwipeToDismiss = true
+ )
+ }
+ }
+ }
+ }
+
private fun canBeRemoved(data: MediaData): Boolean {
return data.isPlaying?.let { !it } ?: data.isClearable && !data.active
}
@@ -315,4 +480,83 @@
private fun isMediaFromRec(data: MediaData): Boolean {
return data.isPlaying == true && mediaFromRecPackageName == data.packageName
}
+
+ /** Log all media cards if smartspace logging is enabled for each. */
+ private fun logSmartspaceAllMediaCards(receivedSmartspaceCardLatency: Int) {
+ sortedMedia.values.forEachIndexed { index, mediaCommonModel ->
+ if (mediaCommonModel is MediaCommonModel.MediaControl) {
+ _selectedUserEntries.value[mediaCommonModel.mediaLoadedModel.instanceId]?.let {
+ it.smartspaceId =
+ SmallHash.hash(it.appUid + systemClock.currentTimeMillis().toInt())
+ it.isImpressed = false
+
+ if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
+ smartspaceLogger.logSmartspaceCardReceived(
+ it.smartspaceId,
+ it.appUid,
+ cardinality = _currentMedia.value.size,
+ isSsReactivated = mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ rank = index,
+ receivedLatencyMillis = receivedSmartspaceCardLatency,
+ )
+ }
+ }
+ }
+ }
+ }
+
+ private fun logSmartspaceMediaCardUserEvent(
+ instanceId: InstanceId,
+ index: Int,
+ eventId: Int,
+ surface: Int,
+ isReactivated: Boolean,
+ interactedSubCardRank: Int = 0,
+ interactedSubCardCardinality: Int = 0,
+ isSwipeToDismiss: Boolean = false
+ ) {
+ _selectedUserEntries.value[instanceId]?.let {
+ smartspaceLogger.logSmartspaceCardUIEvent(
+ eventId,
+ it.smartspaceId,
+ it.appUid,
+ surface,
+ _currentMedia.value.size,
+ isSsReactivated = isReactivated,
+ interactedSubcardRank = interactedSubCardRank,
+ interactedSubcardCardinality = interactedSubCardCardinality,
+ rank = index,
+ isSwipeToDismiss = isSwipeToDismiss,
+ )
+ }
+ }
+
+ private fun logSmarspaceRecommendationCardUserEvent(
+ eventId: Int,
+ surface: Int,
+ index: Int,
+ interactedSubCardRank: Int = 0,
+ interactedSubCardCardinality: Int = 0,
+ isSwipeToDismiss: Boolean = false
+ ) {
+ smartspaceLogger.logSmartspaceCardUIEvent(
+ eventId,
+ SmallHash.hash(_smartspaceMediaData.value.targetId),
+ _smartspaceMediaData.value.getUid(applicationContext),
+ surface,
+ _currentMedia.value.size,
+ isRecommendationCard = true,
+ interactedSubcardRank = interactedSubCardRank,
+ interactedSubcardCardinality = interactedSubCardCardinality,
+ rank = index,
+ isSwipeToDismiss = isSwipeToDismiss,
+ )
+ }
+
+ private fun isSmartspaceLoggingEnabled(commonModel: MediaCommonModel, index: Int): Boolean {
+ return sortedMedia.size > index &&
+ (_smartspaceMediaData.value.expiryTimeMs != 0L ||
+ isRecommendationActive() ||
+ commonModel is MediaCommonModel.MediaRecommendations)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index f78a0f9..803e7ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -116,11 +116,12 @@
return
}
- mediaFilterRepository.addSelectedUserMediaEntry(data)
+ val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
mediaLoadingLogger.logMediaLoaded(data.instanceId, data.active, "loading media")
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId)
+ MediaDataLoadingModel.Loaded(data.instanceId),
+ isUpdate
)
// Notify listeners
@@ -180,7 +181,13 @@
mediaData.instanceId
)
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(lastActiveId)
+ MediaDataLoadingModel.Loaded(
+ lastActiveId,
+ receivedSmartspaceCardLatency =
+ (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
+ .toInt(),
+ isSsReactivated = true
+ )
)
mediaLoadingLogger.logMediaLoaded(
mediaData.instanceId,
@@ -317,9 +324,10 @@
mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- mediaFilterRepository.addSelectedUserMediaEntry(data)
+ val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId)
+ MediaDataLoadingModel.Loaded(data.instanceId),
+ isUpdate
)
mediaLoadingLogger.logMediaLoaded(
data.instanceId,
@@ -332,8 +340,9 @@
}
/** Invoked when the user has dismissed the media carousel */
- fun onSwipeToDismiss() {
+ fun onSwipeToDismiss(surface: Int) {
if (DEBUG) Log.d(TAG, "Media carousel swiped away")
+ mediaFilterRepository.logSmartspaceCardsOnSwipeToDismiss(surface)
val mediaEntries = mediaFilterRepository.allUserEntries.value.entries
mediaEntries.forEach { (key, data) ->
if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 37dffd1..adcfba7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -86,6 +86,7 @@
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
+import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.res.R
@@ -721,6 +722,7 @@
appUid = appUid,
isExplicit = isExplicit,
resumeProgress = progress,
+ smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
)
)
}
@@ -902,6 +904,7 @@
instanceId = instanceId,
appUid = appUid,
isExplicit = isExplicit,
+ smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 8b2f619..eab0d48 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -354,10 +354,21 @@
activeDevice =
routingSession?.let {
+ val icon = if (it.selectedRoutes.size > 1) {
+ context.getDrawable(
+ com.android.settingslib.R.drawable.ic_media_group_device)
+ } else {
+ connectedDevice?.icon // Single route. We don't change the icon.
+ }
// For a remote session, always use the current device from
- // LocalMediaManager. Override with routing session name if available to
- // show dynamic group name.
- connectedDevice?.copy(name = it.name ?: connectedDevice.name)
+ // LocalMediaManager. Override with routing session information if
+ // available:
+ // - Name: To show the dynamic group name.
+ // - Icon: To show the group icon if there's more than one selected
+ // route.
+ connectedDevice?.copy(
+ name = it.name ?: connectedDevice.name,
+ icon = icon)
} ?: MediaDeviceData(
enabled = false,
icon = context.getDrawable(R.drawable.ic_media_home_devices),
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 0630cbd..9d7160c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -36,6 +36,7 @@
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import java.io.PrintWriter
import javax.inject.Inject
@@ -204,11 +205,14 @@
mediaDataProcessor.setMediaResumptionEnabled(isEnabled)
}
- override fun onSwipeToDismiss() {
- mediaDataFilter.onSwipeToDismiss()
+ override fun onSwipeToDismiss() = unsupported
+
+ fun onSwipeToDismiss(location: Int) {
+ mediaDataFilter.onSwipeToDismiss(MediaSmartspaceLogger.getSurface(location))
}
- override fun hasActiveMediaOrRecommendation() = hasActiveMediaOrRecommendation.value
+ override fun hasActiveMediaOrRecommendation() =
+ mediaFilterRepository.hasActiveMediaOrRecommendation()
override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value
@@ -222,6 +226,14 @@
mediaFilterRepository.setOrderedMedia()
}
+ fun logSmartspaceSeenCard(visibleIndex: Int, location: Int, isMediaCardUpdate: Boolean) {
+ mediaFilterRepository.logSmartspaceCardSeen(
+ MediaSmartspaceLogger.getSurface(location),
+ visibleIndex,
+ isMediaCardUpdate
+ )
+ }
+
/** Add a listener for internal events. */
private fun addInternalListener(listener: MediaDataManager.Listener) =
mediaDataProcessor.addInternalListener(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 3f75938..245f6f8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -19,7 +19,6 @@
import android.app.ActivityOptions
import android.app.BroadcastOptions
import android.app.PendingIntent
-import android.content.Context
import android.content.Intent
import android.media.session.MediaSession
import android.provider.Settings
@@ -31,11 +30,11 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.bluetooth.BroadcastDialogController
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaControlModel
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.dialog.MediaOutputDialogManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -50,9 +49,8 @@
class MediaControlInteractor
@AssistedInject
constructor(
- @Application applicationContext: Context,
@Assisted private val instanceId: InstanceId,
- repository: MediaFilterRepository,
+ private val repository: MediaFilterRepository,
private val mediaDataProcessor: MediaDataProcessor,
private val keyguardStateController: KeyguardStateController,
private val activityStarter: ActivityStarter,
@@ -72,8 +70,11 @@
fun removeMediaControl(
token: MediaSession.Token?,
instanceId: InstanceId,
- delayMs: Long
+ delayMs: Long,
+ eventId: Int,
+ location: Int
): Boolean {
+ logSmartspaceUserEvent(eventId, location)
val dismissed =
mediaDataProcessor.dismissMediaData(instanceId, delayMs, userInitiated = true)
if (!dismissed) {
@@ -114,7 +115,13 @@
activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
}
- fun startClickIntent(expandable: Expandable, clickIntent: PendingIntent) {
+ fun startClickIntent(
+ expandable: Expandable,
+ clickIntent: PendingIntent,
+ eventId: Int,
+ location: Int
+ ) {
+ logSmartspaceUserEvent(eventId, location)
if (!launchOverLockscreen(clickIntent)) {
activityStarter.postStartActivityDismissingKeyguard(
clickIntent,
@@ -176,6 +183,14 @@
)
}
+ fun logSmartspaceUserEvent(eventId: Int, location: Int) {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ instanceId = instanceId
+ )
+ }
+
private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? {
return dialogTransitionController(
cuj =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
index dd6b264..c3a36b2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
@@ -31,6 +31,7 @@
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.plugins.ActivityStarter
import java.net.URISyntaxException
import javax.inject.Inject
@@ -67,7 +68,14 @@
val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
- fun removeMediaRecommendations(key: String, dismissIntent: Intent?, delayMs: Long) {
+ fun removeMediaRecommendations(
+ key: String,
+ dismissIntent: Intent?,
+ delayMs: Long,
+ eventId: Int,
+ location: Int
+ ) {
+ logSmartspaceCardUserEvent(eventId, location)
mediaDataProcessor.dismissSmartspaceRecommendation(key, delayMs)
if (dismissIntent == null) {
Log.w(TAG, "Cannot create dismiss action click action: extras missing dismiss_intent.")
@@ -87,7 +95,25 @@
activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
}
- fun startClickIntent(expandable: Expandable, intent: Intent) {
+ fun startClickIntent(
+ expandable: Expandable,
+ intent: Intent,
+ eventId: Int,
+ location: Int,
+ interactedSubCardRank: Int,
+ interactedSubCardCardinality: Int
+ ) {
+ if (interactedSubCardRank == -1) {
+ logSmartspaceCardUserEvent(eventId, MediaSmartspaceLogger.getSurface(location))
+ } else {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ interactedSubCardRank = interactedSubCardRank,
+ interactedSubCardCardinality = interactedSubCardCardinality,
+ isRec = true
+ )
+ }
if (shouldActivityOpenInForeground(intent)) {
// Request to unlock the device if the activity needs to be opened in foreground.
activityStarter.postStartActivityDismissingKeyguard(
@@ -103,6 +129,14 @@
}
}
+ private fun logSmartspaceCardUserEvent(eventId: Int, location: Int) {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ isRec = true
+ )
+ }
+
/** Returns if the action will open the activity in foreground. */
private fun shouldActivityOpenInForeground(intent: Intent): Boolean {
val intentString = intent.extras?.getString(EXTRAS_SMARTSPACE_INTENT) ?: return false
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
index 56cc618..3d5d47b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
@@ -22,6 +22,7 @@
val mediaLoadedModel: MediaDataLoadingModel.Loaded,
val canBeRemoved: Boolean = false,
val isMediaFromRec: Boolean = false,
+ val updateTime: Long = 0L,
) : MediaCommonModel()
data class MediaRecommendations(val recsLoadingModel: SmartspaceMediaLoadingModel) :
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
index 11a5629..40b3477 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
@@ -99,6 +99,12 @@
/** Track progress (0 - 1) to display for players where [resumption] is true */
val resumeProgress: Double? = null,
+
+ /** Smartspace Id, used for logging. */
+ var smartspaceId: Int = -1,
+
+ /** If media card was visible to user, used for logging. */
+ var isImpressed: Boolean = false,
) {
companion object {
/** Media is playing on the local device */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
index 170f1f7..c8a02fa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
@@ -27,6 +27,8 @@
data class Loaded(
override val instanceId: InstanceId,
val immediatelyUpdateUi: Boolean = true,
+ val receivedSmartspaceCardLatency: Int = 0,
+ val isSsReactivated: Boolean = false,
) : MediaDataLoadingModel()
/** Media data has been removed. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
index 9e15dbb..96c3fa8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
@@ -48,6 +48,8 @@
val instanceId: InstanceId? = null,
/** The timestamp in milliseconds indicating when the card should be removed */
val expiryTimeMs: Long = 0L,
+ /** If recommendation card was visible to user, used for logging. */
+ var isImpressed: Boolean = false,
) {
/**
* Indicates if all the data is valid.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 72fb218..62759a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -109,6 +109,10 @@
mainDispatcher: CoroutineDispatcher,
mediaFlags: MediaFlags,
) {
+ // Set up media control location and its listener.
+ viewModel.onLocationChanged(viewController.currentEndLocation)
+ viewController.locationChangeListener = viewModel.onLocationChanged
+
with(viewHolder) {
// AlbumView uses a hardware layer so that clipping of the foreground is handled with
// clipping the album art. Otherwise album art shows through at the edges.
@@ -221,7 +225,7 @@
dismiss.isEnabled = model.isDismissEnabled
dismiss.setOnClickListener {
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- model.onDismissClicked.invoke()
+ model.onDismissClicked()
}
}
cancelText.background = model.cancelTextBackground
@@ -349,7 +353,7 @@
if (actionViewModel.isEnabled) {
button.setOnClickListener {
if (!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) {
- actionViewModel.onClicked.invoke(it.id)
+ actionViewModel.onClicked(it.id)
viewController.multiRippleController.play(
createTouchRippleAnimation(
@@ -469,8 +473,7 @@
transitionDrawable.startTransition(
if (viewModel.shouldAddGradient) 333 else 80
)
- }
- ?: albumView.setImageDrawable(artwork)
+ } ?: albumView.setImageDrawable(artwork)
}
viewController.isArtworkBound = viewModel.shouldAddGradient
viewController.prevArtwork = artwork
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
index bd4d435..5e8a879 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
@@ -71,14 +71,18 @@
fun bindRecsCard(
viewHolder: RecommendationViewHolder,
viewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
+ viewController: MediaViewController,
falsingManager: FalsingManager,
) {
+ // Set up media control location and its listener.
+ viewModel.onLocationChanged(viewController.currentEndLocation)
+ viewController.locationChangeListener = viewModel.onLocationChanged
+
// Bind main card.
viewHolder.cardTitle.setTextColor(viewModel.cardTitleColor)
viewHolder.recommendations.backgroundTintList = ColorStateList.valueOf(viewModel.cardColor)
viewHolder.recommendations.contentDescription =
- viewModel.contentDescription.invoke(mediaViewController.isGutsVisible)
+ viewModel.contentDescription.invoke(viewController.isGutsVisible)
viewHolder.recommendations.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
@@ -88,21 +92,21 @@
viewHolder.recommendations.setOnLongClickListener {
if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
return@setOnLongClickListener true
- if (!mediaViewController.isGutsVisible) {
- openGuts(viewHolder, viewModel, mediaViewController)
+ if (!viewController.isGutsVisible) {
+ openGuts(viewHolder, viewModel, viewController)
} else {
- closeGuts(viewHolder, viewModel, mediaViewController)
+ closeGuts(viewHolder, viewModel, viewController)
}
return@setOnLongClickListener true
}
// Bind all recommendations.
bindRecommendationsList(viewHolder, viewModel.mediaRecs, falsingManager)
- updateRecommendationsVisibility(mediaViewController, viewHolder.recommendations)
+ updateRecommendationsVisibility(viewController, viewHolder.recommendations)
// Set visibility of recommendations.
- val expandedSet: ConstraintSet = mediaViewController.expandedLayout
- val collapsedSet: ConstraintSet = mediaViewController.collapsedLayout
+ val expandedSet: ConstraintSet = viewController.expandedLayout
+ val collapsedSet: ConstraintSet = viewController.collapsedLayout
viewHolder.mediaTitles.forEach {
setVisibleAndAlpha(expandedSet, it.id, viewModel.areTitlesVisible)
setVisibleAndAlpha(collapsedSet, it.id, viewModel.areTitlesVisible)
@@ -112,15 +116,15 @@
setVisibleAndAlpha(collapsedSet, it.id, viewModel.areSubtitlesVisible)
}
- bindRecommendationsGuts(viewHolder, viewModel, mediaViewController, falsingManager)
+ bindRecommendationsGuts(viewHolder, viewModel, viewController, falsingManager)
- mediaViewController.refreshState()
+ viewController.refreshState()
}
private fun bindRecommendationsGuts(
viewHolder: RecommendationViewHolder,
viewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
+ viewController: MediaViewController,
falsingManager: FalsingManager,
) {
val gutsViewHolder = viewHolder.gutsViewHolder
@@ -131,14 +135,14 @@
gutsViewHolder.dismiss.isEnabled = true
gutsViewHolder.dismiss.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- closeGuts(viewHolder, viewModel, mediaViewController)
- gutsViewModel.onDismissClicked.invoke()
+ closeGuts(viewHolder, viewModel, viewController)
+ gutsViewModel.onDismissClicked()
}
gutsViewHolder.cancelText.background = gutsViewModel.cancelTextBackground
gutsViewHolder.cancel.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts(viewHolder, viewModel, mediaViewController)
+ closeGuts(viewHolder, viewModel, viewController)
}
}
@@ -173,7 +177,7 @@
val mediaCoverContainer = viewHolder.mediaCoverContainers[index]
mediaCoverContainer.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- mediaRecViewModel.onClicked.invoke(Expandable.fromView(it), index)
+ mediaRecViewModel.onClicked(Expandable.fromView(it), index)
}
mediaCoverContainer.setOnLongClickListener {
if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 53794d2..125f973 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -42,6 +42,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -106,6 +107,7 @@
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -123,6 +125,7 @@
class MediaCarouselController
@Inject
constructor(
+ @Application applicationScope: CoroutineScope,
private val context: Context,
private val mediaControlPanelFactory: Provider<MediaControlPanel>,
private val visualStabilityProvider: VisualStabilityProvider,
@@ -387,18 +390,18 @@
repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForAnyStateToGoneKeyguardTransition(this)
listenForAnyStateToLockscreenTransition(this)
- listenForLockscreenSettingChanges(this)
if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle
listenForMediaItemsChanges(this)
}
}
+ listenForLockscreenSettingChanges(applicationScope)
// Notifies all active players about animation scale changes.
bgExecutor.execute {
globalSettings.registerContentObserverSync(
- Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
- animationScaleObserver
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ animationScaleObserver
)
}
}
@@ -696,6 +699,7 @@
.onStart { emit(Unit) }
.map { getMediaLockScreenSetting() }
.distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
.collectLatest {
allowMediaPlayerOnLockScreen = it
updateHostVisibility()
@@ -764,6 +768,7 @@
mediaContent.addView(viewHolder.recommendations, position)
}
}
+ onAddOrUpdateVisibleToUserCard(position, isMediaCardUpdate = false)
viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
controllerByViewModel[commonViewModel] = viewController
updateViewControllerToState(viewController, noAnimation = true)
@@ -781,10 +786,14 @@
commonViewModel.onAdded(commonViewModel)
}
- private fun onUpdated(commonViewModel: MediaCommonViewModel) {
+ private fun onUpdated(commonViewModel: MediaCommonViewModel, position: Int) {
commonViewModel.onUpdated(commonViewModel)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
+ onAddOrUpdateVisibleToUserCard(
+ position,
+ commonViewModel is MediaCommonViewModel.MediaControl
+ )
}
private fun onRemoved(commonViewModel: MediaCommonViewModel) {
@@ -821,6 +830,20 @@
mediaCarouselScrollHandler.onPlayersChanged()
}
+ private fun onAddOrUpdateVisibleToUserCard(position: Int, isMediaCardUpdate: Boolean) {
+ if (
+ mediaCarouselScrollHandler.visibleToUser &&
+ mediaCarouselScrollHandler.visibleMediaIndex == position
+ ) {
+ mediaCarouselViewModel.onCardVisibleToUser(
+ mediaCarouselScrollHandler.qsExpanded,
+ mediaCarouselScrollHandler.visibleMediaIndex,
+ currentEndLocation,
+ isMediaCardUpdate
+ )
+ }
+ }
+
private fun setNewViewModelsList(viewModels: List<MediaCommonViewModel>) {
commonViewModels.clear()
commonViewModels.addAll(viewModels)
@@ -884,8 +907,7 @@
val previousVisibleIndex =
MediaPlayerData.playerKeys().indexOfFirst { key -> it == key }
mediaCarouselScrollHandler.scrollToPlayer(previousVisibleIndex, mediaIndex)
- }
- ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
+ } ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
}
} else if (isRtl && mediaContent.childCount > 0) {
// In RTL, Scroll to the first player as it is the rightmost player in media carousel.
@@ -1435,6 +1457,14 @@
/** Log the user impression for media card at visibleMediaIndex. */
fun logSmartspaceImpression(qsExpanded: Boolean) {
+ if (SceneContainerFlag.isEnabled) {
+ mediaCarouselViewModel.onCardVisibleToUser(
+ qsExpanded,
+ mediaCarouselScrollHandler.visibleMediaIndex,
+ currentEndLocation
+ )
+ return
+ }
val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
if (MediaPlayerData.players().size > visibleMediaIndex) {
val mediaControlPanel = MediaPlayerData.getMediaControlPanel(visibleMediaIndex)
@@ -1547,7 +1577,7 @@
@VisibleForTesting
fun onSwipeToDismiss() {
if (mediaFlags.isSceneContainerEnabled()) {
- mediaCarouselViewModel.onSwipeToDismiss()
+ mediaCarouselViewModel.onSwipeToDismiss(currentEndLocation)
return
}
MediaPlayerData.players().forEachIndexed { index, it ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 5ec4f88..846c596 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
import static com.android.settingslib.flags.Flags.legacyLeAudioSharing;
+import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
import android.animation.Animator;
@@ -577,13 +578,23 @@
&& mActivityIntentHelper.wouldPendingShowOverLockscreen(clickIntent,
mLockscreenUserManager.getCurrentUserId());
if (showOverLockscreen) {
- try {
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- clickIntent.send(opts.toBundle());
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent for " + key + " was cancelled");
+ if (mediaLockscreenLaunchAnimation()) {
+ mActivityStarter.startPendingIntentMaybeDismissingKeyguard(
+ clickIntent,
+ /* dismissShade = */ true,
+ /* intentSentUiThreadCallback = */ null,
+ buildLaunchAnimatorController(mMediaViewHolder.getPlayer()),
+ /* fillIntent = */ null,
+ /* extraOptions = */ null);
+ } else {
+ try {
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ clickIntent.send(opts.toBundle());
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent for " + key + " was cancelled");
+ }
}
} else {
mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 9d07232..681bf39 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -103,6 +103,7 @@
lateinit var sizeChangedListener: () -> Unit
lateinit var configurationChangeListener: () -> Unit
lateinit var recsConfigurationChangeListener: (MediaViewController, TransitionLayout) -> Unit
+ var locationChangeListener: (Int) -> Unit = {}
private var firstRefresh: Boolean = true
@VisibleForTesting private var transitionLayout: TransitionLayout? = null
private val layoutController = TransitionLayoutController()
@@ -119,7 +120,15 @@
* The ending location of the view where it ends when all animations and transitions have
* finished
*/
- @MediaLocation var currentEndLocation: Int = -1
+ @MediaLocation
+ var currentEndLocation: Int = -1
+ set(value) {
+ if (field != value) {
+ field = value
+ if (!mediaFlags.isSceneContainerEnabled()) return
+ locationChangeListener(value)
+ }
+ }
/** The starting location of the view where it starts for all animations and transitions */
@MediaLocation private var currentStartLocation: Int = -1
@@ -799,7 +808,7 @@
fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
if (!mediaFlags.isSceneContainerEnabled()) return
seekBarViewModel.logSeek = onSeek
- onBindSeekBar.invoke(seekBarViewModel)
+ onBindSeekBar(seekBarViewModel)
}
fun setUpTurbulenceNoise() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
index 952b134..f28edd6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
@@ -54,7 +54,8 @@
oldItem is MediaCommonViewModel.MediaControl &&
newItem is MediaCommonViewModel.MediaControl
) {
- oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi
+ oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi &&
+ oldItem.updateTime == newItem.updateTime
} else if (
oldItem is MediaCommonViewModel.MediaRecommendations &&
newItem is MediaCommonViewModel.MediaRecommendations
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
index bd81e44..709723f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
@@ -24,7 +24,7 @@
private val old: List<MediaCommonViewModel>,
private val new: List<MediaCommonViewModel>,
private val onAdded: (MediaCommonViewModel, Int) -> Unit,
- private val onUpdated: (MediaCommonViewModel) -> Unit,
+ private val onUpdated: (MediaCommonViewModel, Int) -> Unit,
private val onRemoved: (MediaCommonViewModel) -> Unit,
private val onMoved: (MediaCommonViewModel, Int, Int) -> Unit,
) : ListUpdateCallback {
@@ -47,7 +47,7 @@
override fun onChanged(position: Int, count: Int, payload: Any?) {
for (i in position until position + count) {
- onUpdated(new[i])
+ onUpdated(new[i], position)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index f0d8df5..c453a21 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -98,9 +98,9 @@
private var allowReorder = false
- fun onSwipeToDismiss() {
+ fun onSwipeToDismiss(location: Int) {
logger.logSwipeDismiss()
- interactor.onSwipeToDismiss()
+ interactor.onSwipeToDismiss(location)
}
fun onReorderingAllowed() {
@@ -108,12 +108,24 @@
interactor.reorderMedia()
}
+ fun onCardVisibleToUser(
+ qsExpanded: Boolean,
+ visibleIndex: Int,
+ location: Int,
+ isUpdate: Boolean = false
+ ) {
+ // Skip logging if on LS or QQS, and there is no active media card
+ if (!qsExpanded && !interactor.hasActiveMediaOrRecommendation()) return
+ interactor.logSmartspaceSeenCard(visibleIndex, location, isUpdate)
+ }
+
private fun toViewModel(
commonModel: MediaCommonModel.MediaControl
): MediaCommonViewModel.MediaControl {
val instanceId = commonModel.mediaLoadedModel.instanceId
return mediaControlByInstanceId[instanceId]?.copy(
- immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi
+ immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
+ updateTime = commonModel.updateTime
)
?: MediaCommonViewModel.MediaControl(
instanceId = instanceId,
@@ -125,7 +137,8 @@
mediaControlByInstanceId.remove(instanceId)
},
onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) },
- isMediaFromRec = commonModel.isMediaFromRec
+ isMediaFromRec = commonModel.isMediaFromRec,
+ updateTime = commonModel.updateTime
)
.also { mediaControlByInstanceId[instanceId] = it }
}
@@ -175,7 +188,6 @@
commonViewModel: MediaCommonViewModel,
commonModel: MediaCommonModel.MediaControl
) {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_RECEIVED)
if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) {
// This media control is due for removal as it is now paused + timed out, and resumption
// setting is off.
@@ -196,8 +208,6 @@
if (!mediaFlags.isPersistentSsCardEnabled()) {
commonViewModel.onRemoved(true)
}
- } else {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_RECEIVED)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
index a96d75c..52cb173 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
@@ -33,6 +33,7 @@
override val onRemoved: (Boolean) -> Unit,
override val onUpdated: (MediaCommonViewModel) -> Unit,
val isMediaFromRec: Boolean = false,
+ val updateTime: Long = 0,
) : MediaCommonViewModel()
data class MediaRecommendations(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 099991d..64820e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -37,6 +37,8 @@
import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
@@ -72,6 +74,7 @@
private var isPlaying = false
private var isAnyButtonClicked = false
+ private var location = -1
private fun onDismissMediaData(
token: Token?,
@@ -80,7 +83,13 @@
instanceId: InstanceId
) {
logger.logLongPressDismiss(uid, packageName, instanceId)
- interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY)
+ interactor.removeMediaControl(
+ token,
+ instanceId,
+ MEDIA_PLAYER_ANIMATION_DELAY,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ location
+ )
}
private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? {
@@ -100,7 +109,7 @@
TAG,
Style.CONTENT
)
- ?: return null
+ ?: return null
val gutsViewModel = toGutsViewModel(model, scheme)
@@ -144,8 +153,12 @@
onClicked = { expandable ->
model.clickIntent?.let { clickIntent ->
logger.logTapContentView(model.uid, model.packageName, model.instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
- interactor.startClickIntent(expandable, clickIntent)
+ interactor.startClickIntent(
+ expandable,
+ clickIntent,
+ SMARTSPACE_CARD_CLICK_EVENT,
+ location
+ )
}
},
onLongClicked = {
@@ -153,7 +166,7 @@
},
onSeek = {
logger.logSeek(model.uid, model.packageName, model.instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ interactor.logSmartspaceUserEvent(SMARTSPACE_CARD_CLICK_EVENT, location)
},
onBindSeekbar = { seekBarViewModel ->
if (model.isResume && model.resumeProgress != null) {
@@ -163,7 +176,8 @@
seekBarViewModel.updateController(mediaController)
}
}
- }
+ },
+ onLocationChanged = { location = it }
)
}
@@ -179,8 +193,7 @@
it,
applicationContext.getString(R.string.broadcasting_description_is_broadcasting)
)
- }
- ?: false
+ } ?: false
val useDisabledAlpha =
if (showBroadcastButton) {
!isCurrentBroadcastApp
@@ -197,7 +210,8 @@
return MediaOutputSwitcherViewModel(
isTapEnabled = showBroadcastButton || !useDisabledAlpha,
deviceString = deviceString,
- deviceIcon = device?.icon?.let { Icon.Loaded(it, null) }
+ deviceIcon =
+ device?.icon?.let { Icon.Loaded(it, null) }
?: if (showBroadcastButton) {
Icon.Resource(R.drawable.settings_input_antenna, null)
} else {
@@ -364,7 +378,7 @@
action: Runnable
) {
logger.logTapAction(id, uid, packageName, instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ interactor.logSmartspaceUserEvent(SMARTSPACE_CARD_CLICK_EVENT, location)
isAnyButtonClicked = true
action.run()
}
@@ -385,8 +399,7 @@
SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch { id: Int ->
semanticActions.getActionById(id) != null
}
- }
- ?: false
+ } ?: false
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index 4334341..96e7fc7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -42,4 +42,5 @@
val onLongClicked: () -> Unit,
val onSeek: () -> Unit,
val onBindSeekbar: (SeekBarViewModel) -> Unit,
+ val onLocationChanged: (Int) -> Unit,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
index 52c4bc5..1fd9c4f0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
@@ -37,6 +37,7 @@
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
+import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
@@ -44,6 +45,8 @@
import com.android.systemui.media.controls.ui.controller.MediaViewController.Companion.GUTS_ANIMATION_DURATION
import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
import com.android.systemui.media.controls.util.MediaDataUtils
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
@@ -78,6 +81,8 @@
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ private var location = -1
+
/**
* Called whenever the recommendation has been expired or removed by the user. This method
* removes the recommendation card entirely from the carousel.
@@ -89,9 +94,14 @@
dismissIntent: Intent?,
instanceId: InstanceId?
) {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_DISMISS_EVENT).
logger.logLongPressDismiss(uid, packageName, instanceId)
- interactor.removeMediaRecommendations(key, dismissIntent, GUTS_DISMISS_DELAY_MS_DURATION)
+ interactor.removeMediaRecommendations(
+ key,
+ dismissIntent,
+ GUTS_DISMISS_DELAY_MS_DURATION,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ location
+ )
}
private fun onClicked(
@@ -111,12 +121,18 @@
} else {
logger.logRecommendationItemTap(packageName, instanceId, index)
}
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT).
// set the package name of the player added by recommendation once the media is loaded.
interactor.switchToMediaControl(packageName)
- interactor.startClickIntent(expandable, intent)
+ interactor.startClickIntent(
+ expandable,
+ intent,
+ SMARTSPACE_CARD_CLICK_EVENT,
+ location,
+ index,
+ NUM_REQUIRED_RECOMMENDATIONS
+ )
}
private suspend fun toRecsViewModel(model: MediaRecommendationsModel): MediaRecsCardViewModel? {
@@ -212,6 +228,7 @@
areTitlesVisible = areTitlesVisible,
areSubtitlesVisible = areSubtitlesVisible,
gutsMenu = toGutsViewModel(model, scheme),
+ onLocationChanged = { location = it }
)
}
@@ -259,8 +276,7 @@
width,
height
)
- }
- ?: ColorDrawable(Color.TRANSPARENT)
+ } ?: ColorDrawable(Color.TRANSPARENT)
}
private fun addGradientToRecommendationAlbum(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
index d1713b5..5ecbcb2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
@@ -30,4 +30,5 @@
val areTitlesVisible: Boolean,
val areSubtitlesVisible: Boolean,
val gutsMenu: GutsViewModel,
+ val onLocationChanged: (Int) -> Unit,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
new file mode 100644
index 0000000..9c59aa2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.util
+
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shared.system.SysUiStatsLog
+import javax.inject.Inject
+
+/** Logger class for Smartspace logging events. */
+@SysUISingleton
+class MediaSmartspaceLogger @Inject constructor() {
+ /**
+ * Log Smartspace card received event
+ *
+ * @param instanceId id to uniquely identify a card.
+ * @param uid uid for the application that media comes from.
+ * @param cardinality number of card in carousel.
+ * @param isRecommendationCard whether media card being logged is a recommendations card.
+ * @param isSsReactivated indicates resume media card is reactivated by Smartspace
+ * recommendation signal
+ * @param rank the rank for media card in the media carousel, starting from 0
+ * @param receivedLatencyMillis latency in milliseconds for card received events.
+ */
+ fun logSmartspaceCardReceived(
+ instanceId: Int,
+ uid: Int,
+ cardinality: Int,
+ isRecommendationCard: Boolean = false,
+ isSsReactivated: Boolean = false,
+ rank: Int = 0,
+ receivedLatencyMillis: Int = 0,
+ ) {
+ logSmartspaceCardReported(
+ SMARTSPACE_CARD_RECEIVED_EVENT,
+ instanceId,
+ uid,
+ surfaces =
+ intArrayOf(
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY,
+ ),
+ cardinality,
+ isRecommendationCard,
+ isSsReactivated,
+ rank = rank,
+ receivedLatencyMillis = receivedLatencyMillis,
+ )
+ }
+
+ /**
+ * Log Smartspace card UI event
+ *
+ * @param eventId id of the event. eg: dismiss, click, or seen.
+ * @param instanceId id to uniquely identify a card.
+ * @param uid uid for the application that media comes from.
+ * @param surface location of media carousel holding media card.
+ * @param cardinality number of card in carousel.
+ * @param isRecommendationCard whether media card being logged is a recommendations card.
+ * @param isSsReactivated indicates resume media card is reactivated by Smartspace
+ * recommendation signal
+ * @param rank the rank for media card in the media carousel, starting from 0
+ * @param isSwipeToDismiss whether is to log swipe-to-dismiss event
+ */
+ fun logSmartspaceCardUIEvent(
+ eventId: Int,
+ instanceId: Int,
+ uid: Int,
+ surface: Int,
+ cardinality: Int,
+ isRecommendationCard: Boolean = false,
+ isSsReactivated: Boolean = false,
+ interactedSubcardRank: Int = 0,
+ interactedSubcardCardinality: Int = 0,
+ rank: Int = 0,
+ isSwipeToDismiss: Boolean = false,
+ ) {
+ logSmartspaceCardReported(
+ eventId,
+ instanceId,
+ uid,
+ surfaces = intArrayOf(surface),
+ cardinality,
+ isRecommendationCard,
+ isSsReactivated,
+ interactedSubcardRank,
+ interactedSubcardCardinality,
+ rank = rank,
+ isSwipeToDismiss = isSwipeToDismiss,
+ )
+ }
+
+ /**
+ * Log Smartspace events
+ *
+ * @param eventId UI event id (e.g. 800 for SMARTSPACE_CARD_SEEN)
+ * @param instanceId id to uniquely identify a card, e.g. each headphone generates a new
+ * instanceId
+ * @param uid uid for the application that media comes from
+ * @param surfaces list of display surfaces the media card is on (e.g. lockscreen, shade) when
+ * the event happened
+ * @param cardinality number of card in carousel.
+ * @param isRecommendationCard whether media card being logged is a recommendations card.
+ * @param isSsReactivated indicates resume media card is reactivated by Smartspace
+ * recommendation signal
+ * @param interactedSubcardRank the rank for interacted media item for recommendation card, -1
+ * for tapping on card but not on any media item, 0 for first media item, 1 for second, etc.
+ * @param interactedSubcardCardinality how many media items were shown to the user when there is
+ * user interaction
+ * @param rank the rank for media card in the media carousel, starting from 0
+ * @param receivedLatencyMillis latency in milliseconds for card received events. E.g. latency
+ * between headphone connection to sysUI displays media recommendation card
+ * @param isSwipeToDismiss whether is to log swipe-to-dismiss event
+ */
+ private fun logSmartspaceCardReported(
+ eventId: Int,
+ instanceId: Int,
+ uid: Int,
+ surfaces: IntArray,
+ cardinality: Int,
+ isRecommendationCard: Boolean,
+ isSsReactivated: Boolean,
+ interactedSubcardRank: Int = 0,
+ interactedSubcardCardinality: Int = 0,
+ rank: Int = 0,
+ receivedLatencyMillis: Int = 0,
+ isSwipeToDismiss: Boolean = false,
+ ) {
+ surfaces.forEach { surface ->
+ SysUiStatsLog.write(
+ SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
+ eventId,
+ instanceId,
+ // Deprecated, replaced with AiAi feature type so we don't need to create logging
+ // card type for each new feature.
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
+ surface,
+ // Use -1 as rank value to indicate user swipe to dismiss the card
+ if (isSwipeToDismiss) -1 else rank,
+ cardinality,
+ if (isRecommendationCard) {
+ 15 // MEDIA_RECOMMENDATION
+ } else if (isSsReactivated) {
+ 43 // MEDIA_RESUME_SS_ACTIVATED
+ } else {
+ 31 // MEDIA_RESUME
+ },
+ uid,
+ interactedSubcardRank,
+ interactedSubcardCardinality,
+ receivedLatencyMillis,
+ null, // Media cards cannot have subcards.
+ null // Media cards don't have dimensions today.
+ )
+
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Log Smartspace card event id: $eventId instance id: $instanceId" +
+ " surface: $surface rank: $rank cardinality: $cardinality " +
+ "isRecommendationCard: $isRecommendationCard " +
+ "isSsReactivated: $isSsReactivated" +
+ "uid: $uid " +
+ "interactedSubcardRank: $interactedSubcardRank " +
+ "interactedSubcardCardinality: $interactedSubcardCardinality " +
+ "received_latency_millis: $receivedLatencyMillis"
+ )
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "MediaSmartspaceLogger"
+ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+ private const val SMARTSPACE_CARD_RECEIVED_EVENT = 759
+ const val SMARTSPACE_CARD_CLICK_EVENT = 760
+ const val SMARTSPACE_CARD_DISMISS_EVENT = 761
+ const val SMARTSPACE_CARD_SEEN_EVENT = 800
+
+ /**
+ * Get the location of media view given [currentEndLocation]
+ *
+ * @return location used for Smartspace logging
+ */
+ fun getSurface(location: Int): Int {
+ SceneContainerFlag.isUnexpectedlyInLegacyMode()
+ return when (location) {
+ MediaHierarchyManager.LOCATION_QQS,
+ MediaHierarchyManager.LOCATION_QS -> {
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE
+ }
+ MediaHierarchyManager.LOCATION_LOCKSCREEN -> {
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN
+ }
+ MediaHierarchyManager.LOCATION_DREAM_OVERLAY -> {
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ }
+ else -> SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DEFAULT_SURFACE
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 1e86563..b48b409 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -85,6 +85,7 @@
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.media.dialog.MediaItem.MediaItemType;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.monet.ColorScheme;
import com.android.systemui.plugins.ActivityStarter;
@@ -110,6 +111,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -661,28 +663,38 @@
/* connectedMediaDevice */ null,
devices,
needToHandleMutingExpectedDevice);
+ } else {
+ // selected device exist
+ return categorizeMediaItemsLocked(
+ connectedMediaDevice,
+ devices,
+ /* needToHandleMutingExpectedDevice */ false);
}
- // selected device exist
- return categorizeMediaItemsLocked(
- connectedMediaDevice,
- devices,
- /* needToHandleMutingExpectedDevice */ false);
}
// To keep the same list order
final List<MediaDevice> targetMediaDevices = new ArrayList<>();
final Map<Integer, MediaItem> dividerItems = new HashMap<>();
+
+ Map<String, MediaDevice> idToMediaDeviceMap =
+ devices.stream()
+ .collect(Collectors.toMap(MediaDevice::getId, Function.identity()));
+
for (MediaItem originalMediaItem : oldMediaItems) {
- for (MediaDevice newDevice : devices) {
- if (originalMediaItem.getMediaDevice().isPresent()
- && TextUtils.equals(originalMediaItem.getMediaDevice().get().getId(),
- newDevice.getId())) {
- targetMediaDevices.add(newDevice);
- break;
+ switch (originalMediaItem.getMediaItemType()) {
+ case MediaItemType.TYPE_GROUP_DIVIDER -> {
+ dividerItems.put(
+ oldMediaItems.indexOf(originalMediaItem), originalMediaItem);
}
- }
- if (originalMediaItem.getMediaItemType()
- == MediaItem.MediaItemType.TYPE_GROUP_DIVIDER) {
- dividerItems.put(oldMediaItems.indexOf(originalMediaItem), originalMediaItem);
+ case MediaItemType.TYPE_DEVICE -> {
+ String originalMediaItemId =
+ originalMediaItem.getMediaDevice().orElseThrow().getId();
+ if (idToMediaDeviceMap.containsKey(originalMediaItemId)) {
+ targetMediaDevices.add(idToMediaDeviceMap.get(originalMediaItemId));
+ }
+ }
+ case MediaItemType.TYPE_PAIR_NEW_DEVICE -> {
+ // Do nothing.
+ }
}
}
if (targetMediaDevices.size() != devices.size()) {
@@ -723,12 +735,10 @@
finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
} else {
if (device.isSuggestedDevice() && !suggestedDeviceAdded) {
- attachGroupDivider(finalMediaItems, mContext.getString(
- R.string.media_output_group_title_suggested_device));
+ addSuggestedDeviceGroupDivider(finalMediaItems);
suggestedDeviceAdded = true;
} else if (!device.isSuggestedDevice() && !displayGroupAdded) {
- attachGroupDivider(finalMediaItems, mContext.getString(
- R.string.media_output_group_title_speakers_and_displays));
+ addSpeakersAndDisplaysGroupDivider(finalMediaItems);
displayGroupAdded = true;
}
finalMediaItems.add(MediaItem.createDeviceMediaItem(device));
@@ -738,8 +748,17 @@
return finalMediaItems;
}
- private void attachGroupDivider(List<MediaItem> mediaItems, String title) {
- mediaItems.add(MediaItem.createGroupDividerMediaItem(title));
+ private void addSuggestedDeviceGroupDivider(List<MediaItem> mediaItems) {
+ mediaItems.add(
+ MediaItem.createGroupDividerMediaItem(
+ mContext.getString(R.string.media_output_group_title_suggested_device)));
+ }
+
+ private void addSpeakersAndDisplaysGroupDivider(List<MediaItem> mediaItems) {
+ mediaItems.add(
+ MediaItem.createGroupDividerMediaItem(
+ mContext.getString(
+ R.string.media_output_group_title_speakers_and_displays)));
}
private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
index ee816942..47e0691 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogManager.kt
@@ -98,7 +98,7 @@
createAndShow(
packageName = null,
aboveStatusBar = false,
- dialogTransitionAnimatorController = null,
+ dialogTransitionAnimatorController = controller,
includePlaybackAndAppMetadata = false,
userHandle = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index c8e896d..5084944 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -84,9 +84,13 @@
SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it.scene != Scenes.Gone },
SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to
{
- it.scene == Scenes.Lockscreen ||
- it.scene == Scenes.NotificationsShade ||
- it.scene == Scenes.Shade
+ when {
+ it.invisibleDueToOcclusion -> false
+ it.scene == Scenes.Lockscreen -> true
+ it.scene == Scenes.NotificationsShade -> true
+ it.scene == Scenes.Shade -> true
+ else -> false
+ }
},
SYSUI_STATE_QUICK_SETTINGS_EXPANDED to
{
diff --git a/packages/SystemUI/src/com/android/systemui/navigation/data/repository/NavigationRepository.kt b/packages/SystemUI/src/com/android/systemui/navigation/data/repository/NavigationRepository.kt
new file mode 100644
index 0000000..4409e2c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigation/data/repository/NavigationRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.navigation.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class NavigationRepository
+@Inject
+constructor(
+ private val controller: NavigationModeController,
+) {
+
+ /** Whether the current navigation bar mode is edge-to-edge. */
+ val isGesturalMode: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ NavigationModeController.ModeChangedListener { mode ->
+ trySend(QuickStepContract.isGesturalMode(mode))
+ }
+
+ val currentMode = controller.addListener(listener)
+ trySend(QuickStepContract.isGesturalMode(currentMode))
+
+ awaitClose { controller.removeListener(listener) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigation/domain/interactor/NavigationInteractor.kt
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
copy to packages/SystemUI/src/com/android/systemui/navigation/domain/interactor/NavigationInteractor.kt
index 97ceacc..0f9c883 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigation/domain/interactor/NavigationInteractor.kt
@@ -14,13 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.navigation.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.navigation.data.repository.NavigationRepository
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
@SysUISingleton
-class NoopConsistencyInteractor @Inject constructor() : GridTypeConsistencyInteractor {
- override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> = tiles
+class NavigationInteractor
+@Inject
+constructor(
+ repository: NavigationRepository,
+) {
+
+ /** Whether the current navigation bar mode is edge-to-edge. */
+ val isGesturalMode: Flow<Boolean> = repository.isGesturalMode
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 80c4379..b393155 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -27,11 +27,11 @@
import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import android.content.ContentResolver;
import android.content.Context;
@@ -56,6 +56,8 @@
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.systemui.Dumpable;
@@ -64,16 +66,18 @@
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.rotation.RotationPolicyUtil;
+import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -108,6 +112,7 @@
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Executor mMainExecutor;
+ private final Handler mBgHandler;
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@@ -160,13 +165,15 @@
// Listens for changes to display rotation
private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
+ @WorkerThread
@Override
public void onRotationChanged(final int rotation) {
// We need this to be scheduled as early as possible to beat the redrawing of
// window in response to the orientation change.
+ @Nullable Boolean isRotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
mHandler.postAtFrontOfQueue(() -> {
mRotationWatcherRotation = rotation;
- dispatchRotationChanged(rotation);
+ dispatchRotationChanged(rotation, isRotationLocked);
});
}
};
@@ -194,7 +201,8 @@
ConfigurationController configurationController,
DumpManager dumpManager,
CommandQueue commandQueue,
- @Main Executor mainExecutor) {
+ @Main Executor mainExecutor,
+ @Background Handler bgHandler) {
// b/319489709: This component shouldn't be running for a non-primary user
if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) {
Log.wtf(TAG, "Unexpected initialization for non-primary user", new Throwable());
@@ -215,6 +223,7 @@
mDefaultDisplayId = displayTracker.getDefaultDisplayId();
mEdgeBackGestureHandler = edgeBackGestureHandlerFactory.create(context);
mMainExecutor = mainExecutor;
+ mBgHandler = bgHandler;
mNavBarMode = navigationModeController.addListener(this);
mCommandQueue.addCallback(this);
@@ -322,7 +331,13 @@
listener.updateAssistantAvailable(mAssistantAvailable, mLongPressHomeEnabled);
}
listener.updateWallpaperVisibility(mWallpaperVisible, mDefaultDisplayId);
- listener.updateRotationWatcherState(mRotationWatcherRotation);
+
+ mBgHandler.post(() -> {
+ Boolean isRotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
+ mMainExecutor.execute(
+ () -> listener.updateRotationWatcherState(
+ mRotationWatcherRotation, isRotationLocked));
+ });
}
/**
@@ -526,9 +541,9 @@
}
}
- private void dispatchRotationChanged(int rotation) {
+ private void dispatchRotationChanged(int rotation, @Nullable Boolean isRotationLocked) {
for (NavbarTaskbarStateUpdater listener : mStateListeners) {
- listener.updateRotationWatcherState(rotation);
+ listener.updateRotationWatcherState(rotation, isRotationLocked);
}
}
@@ -544,7 +559,7 @@
void updateAccessibilityServicesState();
void updateAssistantAvailable(boolean available, boolean longPressHomeEnabled);
default void updateWallpaperVisibility(boolean visible, int displayId) {}
- default void updateRotationWatcherState(int rotation) {}
+ default void updateRotationWatcherState(int rotation, @Nullable Boolean isRotationLocked) {}
}
/** Data class to help Taskbar/Navbar initiate state correctly when switching between the two.*/
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 07289cb..89dce03 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -48,8 +48,8 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransitions;
import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
@@ -136,6 +136,8 @@
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.rotation.RotationButtonController;
+import com.android.systemui.shared.rotation.RotationPolicyUtil;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -148,7 +150,6 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -366,9 +367,11 @@
}
@Override
- public void updateRotationWatcherState(int rotation) {
+ public void updateRotationWatcherState(
+ int rotation, @Nullable Boolean isRotationLocked) {
if (mIsOnDefaultDisplay && mView != null) {
- mView.getRotationButtonController().onRotationWatcherChanged(rotation);
+ RotationButtonController controller = mView.getRotationButtonController();
+ controller.onRotationWatcherChanged(rotation, isRotationLocked);
if (mView.needsReorient(rotation)) {
repositionNavigationBar(rotation);
}
@@ -819,8 +822,9 @@
// Reset user rotation pref to match that of the WindowManager if starting in locked
// mode. This will automatically happen when switching from auto-rotate to locked mode.
- if (display != null && rotationButtonController.isRotationLocked()) {
- rotationButtonController.setRotationLockedAtAngle(
+ @Nullable Boolean isRotationLocked = RotationPolicyUtil.isRotationLocked(mContext);
+ if (display != null && isRotationLocked) {
+ rotationButtonController.setRotationLockedAtAngle(isRotationLocked,
display.getRotation(), /* caller= */ "NavigationBar#onViewAttached");
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index a601d7f..c801662 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -19,7 +19,7 @@
import androidx.annotation.Nullable;
import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
/** A controller to handle navigation bars. */
public interface NavigationBarController {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
index e73b078..06a78c5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt
@@ -18,7 +18,7 @@
import com.android.internal.statusbar.RegisterStatusBarResult
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.phone.BarTransitions
+import com.android.systemui.shared.statusbar.phone.BarTransitions
import javax.inject.Inject
/** A no-op version of [NavigationBarController] for variants like Arc and TV. */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index b177b0b..f0207aa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -23,6 +23,7 @@
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
+import static com.android.wm.shell.Flags.enableTaskbarOnPhones;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -55,11 +56,11 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.settings.SecureSettings;
@@ -285,8 +286,10 @@
@VisibleForTesting
boolean supportsTaskbar() {
- // Enable for tablets, unfolded state on a foldable device or (non handheld AND flag is set)
- return mIsLargeScreen || (!mIsPhone && enableTaskbarNavbarUnification());
+ // Enable for tablets, unfolded state on a foldable device, (non handheld AND flag is set),
+ // or handheld when enableTaskbarOnPhones() returns true.
+ boolean foldedOrPhone = !mIsPhone || enableTaskbarOnPhones();
+ return mIsLargeScreen || (foldedOrPhone && enableTaskbarNavbarUnification());
}
private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
@@ -428,6 +431,8 @@
NavigationBar navBar = mNavigationBars.get(displayId);
if (navBar != null) {
navBar.checkNavBarModes();
+ } else {
+ mTaskbarDelegate.checkNavBarModes();
}
}
@@ -436,6 +441,8 @@
NavigationBar navBar = mNavigationBars.get(displayId);
if (navBar != null) {
navBar.finishBarAnimations();
+ } else {
+ mTaskbarDelegate.finishBarAnimations();
}
}
@@ -444,6 +451,8 @@
NavigationBar navBar = mNavigationBars.get(displayId);
if (navBar != null) {
navBar.touchAutoDim();
+ } else {
+ mTaskbarDelegate.touchAutoDim();
}
}
@@ -452,6 +461,8 @@
NavigationBar navBar = mNavigationBars.get(displayId);
if (navBar != null) {
navBar.transitionTo(barMode, animate);
+ } else {
+ mTaskbarDelegate.transitionTo(barMode, animate);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 201e586..5442598 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -28,7 +28,7 @@
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index b360af0..d022c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -24,6 +24,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -34,7 +35,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
-import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
@@ -63,13 +63,16 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -117,6 +120,11 @@
boolean longPressHomeEnabled) {
updateAssistantAvailability(available, longPressHomeEnabled);
}
+
+ @Override
+ public void updateWallpaperVisibility(boolean visible, int displayId) {
+ updateWallpaperVisible(displayId, visible);
+ }
};
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
@@ -150,6 +158,7 @@
private final AutoHideUiElement mAutoHideUiElement = new AutoHideUiElement() {
@Override
public void synchronizeState() {
+ checkNavBarModes();
}
@Override
@@ -165,11 +174,13 @@
private BackAnimation mBackAnimation;
- private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final StatusBarStateController mStatusBarStateController;
@Inject
public TaskbarDelegate(Context context,
LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ StatusBarStateController statusBarStateController) {
mLightBarTransitionsControllerFactory = lightBarTransitionsControllerFactory;
mContext = context;
@@ -179,6 +190,7 @@
};
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mStatusBarKeyguardViewManager.setTaskbarDelegate(this);
+ mStatusBarStateController = statusBarStateController;
}
public void setDependencies(CommandQueue commandQueue,
@@ -324,6 +336,68 @@
return (mSysUiState.getFlags() & View.STATUS_BAR_DISABLE_RECENT) == 0;
}
+ void onTransitionModeUpdated(int barMode, boolean checkBarModes) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onTransitionModeUpdated(barMode, checkBarModes);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onTransitionModeUpdated() failed, barMode: " + barMode, e);
+ }
+ }
+
+ void checkNavBarModes() {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().checkNavBarModes();
+ } catch (RemoteException e) {
+ Log.e(TAG, "checkNavBarModes() failed", e);
+ }
+ }
+
+ void finishBarAnimations() {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().finishBarAnimations();
+ } catch (RemoteException e) {
+ Log.e(TAG, "finishBarAnimations() failed", e);
+ }
+ }
+
+ void touchAutoDim() {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ int state = mStatusBarStateController.getState();
+ boolean shouldReset =
+ state != StatusBarState.KEYGUARD && state != StatusBarState.SHADE_LOCKED;
+ mOverviewProxyService.getProxy().touchAutoDim(shouldReset);
+ } catch (RemoteException e) {
+ Log.e(TAG, "touchAutoDim() failed", e);
+ }
+ }
+
+ void transitionTo(@BarTransitions.TransitionMode int barMode, boolean animate) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().transitionTo(barMode, animate);
+ } catch (RemoteException e) {
+ Log.e(TAG, "transitionTo() failed, barMode: " + barMode, e);
+ }
+ }
private void updateAssistantAvailability(boolean assistantAvailable,
boolean longPressHomeEnabled) {
if (mOverviewProxyService.getProxy() == null) {
@@ -338,6 +412,18 @@
}
}
+ private void updateWallpaperVisible(int displayId, boolean visible) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().updateWallpaperVisibility(displayId, visible);
+ } catch (RemoteException e) {
+ Log.e(TAG, "updateWallpaperVisibility() failed, visible: " + visible, e);
+ }
+ }
+
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
@@ -465,9 +551,11 @@
private boolean updateTransitionMode(int barMode) {
if (mTransitionMode != barMode) {
mTransitionMode = barMode;
+ onTransitionModeUpdated(barMode, true);
if (mAutoHideController != null) {
mAutoHideController.touchAutoHide();
}
+
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
index d0c7fbc..1f74716 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
@@ -22,6 +22,8 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeAlignment
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -29,11 +31,19 @@
/** Models UI state and handles user input for the Notifications Shade scene. */
@SysUISingleton
-class NotificationsShadeSceneViewModel @Inject constructor() {
+class NotificationsShadeSceneViewModel
+@Inject
+constructor(
+ shadeInteractor: ShadeInteractor,
+) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
MutableStateFlow(
mapOf(
- Swipe.Up to SceneFamilies.Home,
+ if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
+ Swipe.Up
+ } else {
+ Swipe.Down
+ } to SceneFamilies.Home,
Back to SceneFamilies.Home,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index e77bd03..4018320 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -103,8 +103,7 @@
}
private int mLastMaxHeight = -1;
- @Override
- public void setPageMargin(int marginPixels) {
+ public void setPageMargin(int marginPixelsStart, int marginPixelsEnd) {
// Using page margins creates some rounding issues that interfere with the correct position
// in the onPageChangedListener and therefore present bad positions to the PageIndicator.
// Instead, we use negative margins in the container and positive padding in the pages,
@@ -113,14 +112,19 @@
// QSContainerImpl resources are set onAttachedView, so this view will always have the right
// values when attached.
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
- lp.setMarginStart(-marginPixels);
- lp.setMarginEnd(-marginPixels);
+ lp.setMarginStart(-marginPixelsStart);
+ lp.setMarginEnd(-marginPixelsEnd);
setLayoutParams(lp);
int nPages = mPages.size();
for (int i = 0; i < nPages; i++) {
View v = mPages.get(i);
- v.setPadding(marginPixels, v.getPaddingTop(), marginPixels, v.getPaddingBottom());
+ v.setPadding(
+ marginPixelsStart,
+ v.getPaddingTop(),
+ marginPixelsEnd,
+ v.getPaddingBottom()
+ );
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index cc0901f..db0676e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -35,6 +35,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.LargeScreenHeaderHelper;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.util.LargeScreenUtils;
@@ -300,7 +301,7 @@
// QS panel lays out some of its content full width
qsPanelController.setContentMargins(mContentHorizontalPadding,
mContentHorizontalPadding);
- qsPanelController.setPageMargin(mTilesPageMargin);
+ setPageMargins(qsPanelController);
} else if (view == mHeader) {
quickStatusBarHeaderController.setContentMargins(mContentHorizontalPadding,
mContentHorizontalPadding);
@@ -318,6 +319,18 @@
}
}
+ private void setPageMargins(QSPanelController qsPanelController) {
+ if (SceneContainerFlag.isEnabled()) {
+ if (mHorizontalMargins == mTilesPageMargin * 2 + 1) {
+ qsPanelController.setPageMargin(mTilesPageMargin, mTilesPageMargin + 1);
+ } else {
+ qsPanelController.setPageMargin(mTilesPageMargin, mTilesPageMargin);
+ }
+ } else {
+ qsPanelController.setPageMargin(mTilesPageMargin, mTilesPageMargin);
+ }
+ }
+
/**
* Clip QS bottom using a concave shape.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index abc2b7f..032891f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -638,9 +638,9 @@
return mListening;
}
- protected void setPageMargin(int pageMargin) {
+ protected void setPageMargin(int pageMarginStart, int pageMarginEnd) {
if (mTileLayout instanceof PagedTileLayout) {
- ((PagedTileLayout) mTileLayout).setPageMargin(pageMargin);
+ ((PagedTileLayout) mTileLayout).setPageMargin(pageMarginStart, pageMarginEnd);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f76183e..d5cd8dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -284,8 +284,8 @@
return mView.isExpanded();
}
- void setPageMargin(int pageMargin) {
- mView.setPageMargin(pageMargin);
+ void setPageMargin(int pageMarginStart, int pageMarginEnd) {
+ mView.setPageMargin(pageMarginStart, pageMarginEnd);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 24b7a01..96df728 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -224,6 +224,10 @@
});
}
+ boolean isBound() {
+ return mBound.get();
+ }
+
@WorkerThread
private void setBindService(boolean bind) {
if (mBound.get() && mUnbindImmediate.get()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 6bc5095..d10471d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -217,7 +217,11 @@
Log.e(TAG, "Service already bound");
return;
}
- mPendingBind = true;
+ if (!mStateManager.isBound()) {
+ // If we are bound, we don't need to set a pending bind. There's either one already or
+ // we are fully bound.
+ mPendingBind = true;
+ }
mBound = true;
mJustBound = true;
mHandler.postDelayed(mJustBoundOver, MIN_BIND_TIME);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PaginatedBaseLayoutType.kt
similarity index 76%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PaginatedBaseLayoutType.kt
index d8af3fa..0285cbd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PaginatedBaseLayoutType.kt
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.qs.panels.dagger
-import com.android.systemui.kosmos.Kosmos
+import javax.inject.Qualifier
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class PaginatedBaseLayoutType
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 7b67993..c214361 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -30,18 +30,21 @@
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.IconLabelVisibilityLog
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
import com.android.systemui.qs.panels.shared.model.PartitionedGridLayoutType
import com.android.systemui.qs.panels.shared.model.StretchedGridLayoutType
import com.android.systemui.qs.panels.ui.compose.GridLayout
import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
+import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout
import com.android.systemui.qs.panels.ui.compose.PartitionedGridLayout
import com.android.systemui.qs.panels.ui.compose.StretchedGridLayout
+import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModelImpl
import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModelImpl
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModelImpl
-import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModelImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -62,14 +65,24 @@
@Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
- @Binds fun bindGridSizeViewModel(impl: InfiniteGridSizeViewModelImpl): InfiniteGridSizeViewModel
+ @Binds fun bindGridSizeViewModel(impl: FixedColumnsSizeViewModelImpl): FixedColumnsSizeViewModel
@Binds
fun bindIconLabelVisibilityViewModel(
impl: IconLabelVisibilityViewModelImpl
): IconLabelVisibilityViewModel
- @Binds @Named("Default") fun bindDefaultGridLayout(impl: PartitionedGridLayout): GridLayout
+ @Binds
+ @PaginatedBaseLayoutType
+ fun bindPaginatedBaseGridLayout(impl: PartitionedGridLayout): PaginatableGridLayout
+
+ @Binds
+ @PaginatedBaseLayoutType
+ fun bindPaginatedBaseConsistencyInteractor(
+ impl: NoopGridConsistencyInteractor
+ ): GridTypeConsistencyInteractor
+
+ @Binds @Named("Default") fun bindDefaultGridLayout(impl: PaginatedGridLayout): GridLayout
companion object {
@Provides
@@ -109,6 +122,14 @@
}
@Provides
+ @IntoSet
+ fun providePaginatedGridLayout(
+ gridLayout: PaginatedGridLayout
+ ): Pair<GridLayoutType, GridLayout> {
+ return Pair(PaginatedGridLayoutType, gridLayout)
+ }
+
+ @Provides
fun provideGridLayoutMap(
entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridLayout>>
): Map<GridLayoutType, GridLayout> {
@@ -147,6 +168,14 @@
}
@Provides
+ @IntoSet
+ fun providePaginatedGridConsistencyInteractor(
+ @PaginatedBaseLayoutType consistencyInteractor: GridTypeConsistencyInteractor,
+ ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
+ return Pair(PaginatedGridLayoutType, consistencyInteractor)
+ }
+
+ @Provides
fun provideGridConsistencyInteractorMap(
entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridTypeConsistencyInteractor>>
): Map<GridLayoutType, GridTypeConsistencyInteractor> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepository.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepository.kt
rename to packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepository.kt
index 43ccdf66..32ce973 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepository.kt
@@ -23,7 +23,7 @@
import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
-class InfiniteGridSizeRepository @Inject constructor() {
+class FixedColumnsRepository @Inject constructor() {
// Number of columns in the narrowest state for consistency
private val _columns = MutableStateFlow(4)
val columns: StateFlow<Int> = _columns.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
index 44d8688..47c4ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.shared.model.GridLayoutType
-import com.android.systemui.qs.panels.shared.model.PartitionedGridLayoutType
+import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -26,13 +26,14 @@
interface GridLayoutTypeRepository {
val layout: StateFlow<GridLayoutType>
+
fun setLayout(type: GridLayoutType)
}
@SysUISingleton
class GridLayoutTypeRepositoryImpl @Inject constructor() : GridLayoutTypeRepository {
private val _layout: MutableStateFlow<GridLayoutType> =
- MutableStateFlow(PartitionedGridLayoutType)
+ MutableStateFlow(PaginatedGridLayoutType)
override val layout = _layout.asStateFlow()
override fun setLayout(type: GridLayoutType) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
new file mode 100644
index 0000000..26b2e2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.emitOnStart
+import javax.inject.Inject
+import kotlinx.coroutines.flow.map
+
+/**
+ * Provides the number of [rows] to use with a paginated grid, by tracking the resource
+ * [R.integer.quick_settings_max_rows].
+ */
+@SysUISingleton
+class PaginatedGridRepository
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ configurationRepository: ConfigurationRepository,
+) {
+ val rows =
+ configurationRepository.onConfigurationChange.emitOnStart().map {
+ resources.getInteger(R.integer.quick_settings_max_rows)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractor.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractor.kt
index 13c6072..9591002 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractor.kt
@@ -17,11 +17,11 @@
package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.data.repository.InfiniteGridSizeRepository
+import com.android.systemui.qs.panels.data.repository.FixedColumnsRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
@SysUISingleton
-class InfiniteGridSizeInteractor @Inject constructor(repo: InfiniteGridSizeRepository) {
+class FixedColumnsSizeInteractor @Inject constructor(repo: FixedColumnsRepository) {
val columns: StateFlow<Int> = repo.columns
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
index e99c64c..0fe79af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
@@ -28,7 +28,7 @@
@Inject
constructor(
private val iconTilesInteractor: IconTilesInteractor,
- private val gridSizeInteractor: InfiniteGridSizeInteractor
+ private val gridSizeInteractor: FixedColumnsSizeInteractor
) : GridTypeConsistencyInteractor {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/PaginatedGridInteractor.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/PaginatedGridInteractor.kt
index 97ceacc..d7d1ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/PaginatedGridInteractor.kt
@@ -17,10 +17,14 @@
package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.panels.data.repository.PaginatedGridRepository
import javax.inject.Inject
@SysUISingleton
-class NoopConsistencyInteractor @Inject constructor() : GridTypeConsistencyInteractor {
- override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> = tiles
+class PaginatedGridInteractor
+@Inject
+constructor(paginatedGridRepository: PaginatedGridRepository) {
+ val rows = paginatedGridRepository.rows
+
+ val defaultRows = 4
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
index 9550ddb..b1942fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
@@ -34,3 +34,6 @@
/** Grid type grouping large tiles on top and icon tiles at the bottom. */
data object PartitionedGridLayoutType : GridLayoutType
+
+/** Grid type for a paginated list of tiles. It will delegate to some other layout type. */
+data object PaginatedGridLayoutType : GridLayoutType
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
index 8806931..e2f6bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
@@ -18,15 +18,19 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.TileRow
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
+/** A layout of tiles, indicating how they should be composed when showing in QS or in edit mode. */
interface GridLayout {
@Composable
fun TileGrid(
tiles: List<TileViewModel>,
modifier: Modifier,
+ editModeStart: () -> Unit,
)
@Composable
@@ -37,3 +41,49 @@
onRemoveTile: (TileSpec) -> Unit,
)
}
+
+/**
+ * A type of [GridLayout] that can be paginated, to use together with [PaginatedGridLayout].
+ *
+ * [splitIntoPages] determines how to split a list of tiles based on the number of rows and columns
+ * available.
+ */
+interface PaginatableGridLayout : GridLayout {
+ fun splitIntoPages(
+ tiles: List<TileViewModel>,
+ rows: Int,
+ columns: Int,
+ ): List<List<TileViewModel>>
+
+ companion object {
+
+ /**
+ * Splits a list of [SizedTile] into rows, each with at most [columns] occupied.
+ *
+ * It will leave gaps at the end of a row if the next [SizedTile] has [SizedTile.width] that
+ * is larger than the space remaining in the row.
+ */
+ fun splitInRows(
+ tiles: List<SizedTile<TileViewModel>>,
+ columns: Int
+ ): List<List<SizedTile<TileViewModel>>> {
+ val row = TileRow<TileViewModel>(columns)
+
+ return buildList {
+ for (tile in tiles) {
+ check(tile.width <= columns)
+ if (!row.maybeAddTile(tile)) {
+ // Couldn't add tile to previous row, create a row with the current tiles
+ // and start a new one
+ add(row.tiles)
+ row.clear()
+ row.maybeAddTile(tile)
+ }
+ }
+ if (row.tiles.isNotEmpty()) {
+ add(row.tiles)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index 2f0fe22..ea97f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -26,9 +26,10 @@
import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
@@ -39,13 +40,14 @@
@Inject
constructor(
private val iconTilesViewModel: IconTilesViewModel,
- private val gridSizeViewModel: InfiniteGridSizeViewModel,
-) : GridLayout {
+ private val gridSizeViewModel: FixedColumnsSizeViewModel,
+) : PaginatableGridLayout {
@Composable
override fun TileGrid(
tiles: List<TileViewModel>,
modifier: Modifier,
+ editModeStart: () -> Unit,
) {
DisposableEffect(tiles) {
val token = Any()
@@ -55,16 +57,8 @@
val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
- items(
- tiles.size,
- span = { index ->
- if (iconTilesViewModel.isIconTile(tiles[index].spec)) {
- GridItemSpan(1)
- } else {
- GridItemSpan(2)
- }
- }
- ) { index ->
+ items(tiles.size, span = { index -> GridItemSpan(tiles[index].spec.width()) }) { index
+ ->
Tile(
tile = tiles[index],
iconOnly = iconTilesViewModel.isIconTile(tiles[index].spec),
@@ -92,4 +86,22 @@
onRemoveTile = onRemoveTile,
)
}
+
+ override fun splitIntoPages(
+ tiles: List<TileViewModel>,
+ rows: Int,
+ columns: Int,
+ ): List<List<TileViewModel>> {
+
+ return PaginatableGridLayout.splitInRows(
+ tiles.map { SizedTile(it, it.spec.width()) },
+ columns,
+ )
+ .chunked(rows)
+ .map { it.flatten().map { it.tile } }
+ }
+
+ private fun TileSpec.width(): Int {
+ return if (iconTilesViewModel.isIconTile(this)) 1 else 2
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
new file mode 100644
index 0000000..7de22161
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Arrangement.spacedBy
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.semantics.pageLeft
+import androidx.compose.ui.semantics.pageRight
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import kotlin.math.absoluteValue
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+@Composable
+fun PagerDots(
+ pagerState: PagerState,
+ activeColor: Color,
+ nonActiveColor: Color,
+ modifier: Modifier = Modifier,
+ dotSize: Dp = 6.dp,
+ spaceSize: Dp = 4.dp,
+) {
+ if (pagerState.pageCount < 2) {
+ return
+ }
+ val inPageTransition by
+ remember(pagerState) {
+ derivedStateOf {
+ pagerState.currentPageOffsetFraction.absoluteValue > 0.01 &&
+ !pagerState.isOverscrolling()
+ }
+ }
+ val coroutineScope = rememberCoroutineScope()
+ Row(
+ modifier =
+ modifier
+ .wrapContentWidth()
+ .pagerDotsSemantics(
+ pagerState,
+ coroutineScope,
+ ),
+ horizontalArrangement = spacedBy(spaceSize),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ if (!inPageTransition) {
+ repeat(pagerState.pageCount) { i ->
+ // We use canvas directly to only invalidate the draw phase when the page is
+ // changing.
+ Canvas(Modifier.size(dotSize)) {
+ if (pagerState.currentPage == i) {
+ drawCircle(activeColor)
+ } else {
+ drawCircle(nonActiveColor)
+ }
+ }
+ }
+ } else {
+ val doubleDotWidth = dotSize * 2 + spaceSize
+ val cornerRadius = dotSize / 2
+ val width by
+ animateDpAsState(targetValue = if (inPageTransition) doubleDotWidth else dotSize)
+
+ fun DrawScope.drawDoubleRect() {
+ drawRoundRect(
+ color = activeColor,
+ size = Size(width.toPx(), dotSize.toPx()),
+ cornerRadius = CornerRadius(cornerRadius.toPx(), cornerRadius.toPx())
+ )
+ }
+
+ repeat(pagerState.pageCount) { page ->
+ Canvas(Modifier.size(dotSize)) {
+ val withPrevious = pagerState.currentPageOffsetFraction < 0
+ val ltr = layoutDirection == LayoutDirection.Ltr
+ if (
+ withPrevious && page == (pagerState.currentPage - 1) ||
+ !withPrevious && page == pagerState.currentPage
+ ) {
+ if (ltr) {
+ drawDoubleRect()
+ }
+ } else if (
+ withPrevious && page == pagerState.currentPage ||
+ !withPrevious && page == (pagerState.currentPage + 1)
+ ) {
+ if (!ltr) {
+ drawDoubleRect()
+ }
+ } else {
+ drawCircle(nonActiveColor)
+ }
+ }
+ }
+ }
+ }
+}
+
+private fun Modifier.pagerDotsSemantics(
+ pagerState: PagerState,
+ coroutineScope: CoroutineScope,
+): Modifier {
+ return then(
+ Modifier.semantics {
+ pageLeft {
+ if (pagerState.canScrollBackward) {
+ coroutineScope.launch {
+ pagerState.animateScrollToPage(pagerState.currentPage - 1)
+ }
+ true
+ } else {
+ false
+ }
+ }
+ pageRight {
+ if (pagerState.canScrollForward) {
+ coroutineScope.launch {
+ pagerState.animateScrollToPage(pagerState.currentPage + 1)
+ }
+ true
+ } else {
+ false
+ }
+ }
+ stateDescription = "Page ${pagerState.settledPage + 1} of ${pagerState.pageCount}"
+ }
+ )
+}
+
+private fun PagerState.isOverscrolling(): Boolean {
+ val position = currentPage + currentPageOffsetFraction
+ return position < 0 || position > pageCount - 1
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
new file mode 100644
index 0000000..2ee957e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
+import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
+import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.InterPageSpacing
+import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class PaginatedGridLayout
+@Inject
+constructor(
+ private val viewModel: PaginatedGridViewModel,
+ @PaginatedBaseLayoutType private val delegateGridLayout: PaginatableGridLayout,
+) : GridLayout by delegateGridLayout {
+ @Composable
+ override fun TileGrid(
+ tiles: List<TileViewModel>,
+ modifier: Modifier,
+ editModeStart: () -> Unit,
+ ) {
+ DisposableEffect(tiles) {
+ val token = Any()
+ tiles.forEach { it.startListening(token) }
+ onDispose { tiles.forEach { it.stopListening(token) } }
+ }
+ val columns by viewModel.columns.collectAsStateWithLifecycle()
+ val rows by viewModel.rows.collectAsStateWithLifecycle()
+
+ val pages =
+ remember(tiles, columns, rows) {
+ delegateGridLayout.splitIntoPages(tiles, rows = rows, columns = columns)
+ }
+
+ val pagerState = rememberPagerState(0) { pages.size }
+
+ Column {
+ HorizontalPager(
+ state = pagerState,
+ modifier = Modifier,
+ pageSpacing = if (pages.size > 1) InterPageSpacing else 0.dp,
+ beyondViewportPageCount = 1,
+ verticalAlignment = Alignment.Top,
+ ) {
+ val page = pages[it]
+
+ delegateGridLayout.TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
+ }
+ Box(
+ modifier = Modifier.height(FooterHeight).fillMaxWidth(),
+ ) {
+ PagerDots(
+ pagerState = pagerState,
+ activeColor = MaterialTheme.colorScheme.primary,
+ nonActiveColor = MaterialTheme.colorScheme.surfaceVariant,
+ modifier = Modifier.align(Alignment.Center)
+ )
+ CompositionLocalProvider(value = LocalContentColor provides Color.White) {
+ IconButton(
+ onClick = editModeStart,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ ) {
+ Icon(
+ imageVector = Icons.Default.Edit,
+ contentDescription = stringResource(id = R.string.qs_edit)
+ )
+ }
+ }
+ }
+ }
+ }
+
+ private object Dimensions {
+ val FooterHeight = 48.dp
+ val InterPageSpacing = 16.dp
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
index 9233e76..7f5e474 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
@@ -53,6 +53,7 @@
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.modifiers.background
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.PartitionedGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
@@ -63,9 +64,13 @@
@SysUISingleton
class PartitionedGridLayout @Inject constructor(private val viewModel: PartitionedGridViewModel) :
- GridLayout {
+ PaginatableGridLayout {
@Composable
- override fun TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
+ override fun TileGrid(
+ tiles: List<TileViewModel>,
+ modifier: Modifier,
+ editModeStart: () -> Unit,
+ ) {
DisposableEffect(tiles) {
val token = Any()
tiles.forEach { it.startListening(token) }
@@ -169,6 +174,20 @@
}
}
+ override fun splitIntoPages(
+ tiles: List<TileViewModel>,
+ rows: Int,
+ columns: Int,
+ ): List<List<TileViewModel>> {
+ val (smallTiles, largeTiles) = tiles.partition { viewModel.isIconTile(it.spec) }
+
+ val sizedLargeTiles = largeTiles.map { SizedTile(it, 2) }
+ val sizedSmallTiles = smallTiles.map { SizedTile(it, 1) }
+ val largeTilesRows = PaginatableGridLayout.splitInRows(sizedLargeTiles, columns)
+ val smallTilesRows = PaginatableGridLayout.splitInRows(sizedSmallTiles, columns)
+ return (largeTilesRows + smallTilesRows).chunked(rows).map { it.flatten().map { it.tile } }
+ }
+
@Composable
private fun CurrentTiles(
tiles: List<EditTileViewModel>,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
index 7f4e0a7..4a90102 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
@@ -30,8 +30,8 @@
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.TileRow
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
@@ -42,13 +42,14 @@
@Inject
constructor(
private val iconTilesViewModel: IconTilesViewModel,
- private val gridSizeViewModel: InfiniteGridSizeViewModel,
+ private val gridSizeViewModel: FixedColumnsSizeViewModel,
) : GridLayout {
@Composable
override fun TileGrid(
tiles: List<TileViewModel>,
modifier: Modifier,
+ editModeStart: () -> Unit,
) {
DisposableEffect(tiles) {
val token = Any()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
index 2dab7c3..8c57d41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
@@ -23,9 +23,13 @@
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
@Composable
-fun TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) {
+fun TileGrid(
+ viewModel: TileGridViewModel,
+ modifier: Modifier = Modifier,
+ editModeStart: () -> Unit
+) {
val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList())
- gridLayout.TileGrid(tiles, modifier)
+ gridLayout.TileGrid(tiles, modifier, editModeStart)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModel.kt
similarity index 78%
rename from packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModel.kt
index a4ee58f..865c86b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModel.kt
@@ -17,16 +17,16 @@
package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
+import com.android.systemui.qs.panels.domain.interactor.FixedColumnsSizeInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
-interface InfiniteGridSizeViewModel {
+interface FixedColumnsSizeViewModel {
val columns: StateFlow<Int>
}
@SysUISingleton
-class InfiniteGridSizeViewModelImpl @Inject constructor(interactor: InfiniteGridSizeInteractor) :
- InfiniteGridSizeViewModel {
+class FixedColumnsSizeViewModelImpl @Inject constructor(interactor: FixedColumnsSizeInteractor) :
+ FixedColumnsSizeViewModel {
override val columns: StateFlow<Int> = interactor.columns
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
new file mode 100644
index 0000000..28bf474
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.qs.panels.domain.interactor.PaginatedGridInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class PaginatedGridViewModel
+@Inject
+constructor(
+ iconTilesViewModel: IconTilesViewModel,
+ gridSizeViewModel: FixedColumnsSizeViewModel,
+ iconLabelVisibilityViewModel: IconLabelVisibilityViewModel,
+ paginatedGridInteractor: PaginatedGridInteractor,
+ @Application applicationScope: CoroutineScope,
+) :
+ IconTilesViewModel by iconTilesViewModel,
+ FixedColumnsSizeViewModel by gridSizeViewModel,
+ IconLabelVisibilityViewModel by iconLabelVisibilityViewModel {
+ val rows =
+ paginatedGridInteractor.rows.stateIn(
+ applicationScope,
+ SharingStarted.WhileSubscribed(),
+ paginatedGridInteractor.defaultRows,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt
index 730cf63..2049edb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModel.kt
@@ -24,9 +24,9 @@
@Inject
constructor(
iconTilesViewModel: IconTilesViewModel,
- gridSizeViewModel: InfiniteGridSizeViewModel,
+ gridSizeViewModel: FixedColumnsSizeViewModel,
iconLabelVisibilityViewModel: IconLabelVisibilityViewModel,
) :
IconTilesViewModel by iconTilesViewModel,
- InfiniteGridSizeViewModel by gridSizeViewModel,
+ FixedColumnsSizeViewModel by gridSizeViewModel,
IconLabelVisibilityViewModel by iconLabelVisibilityViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
index b5bef9f..88f7169 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
@@ -41,12 +41,7 @@
override fun ProducerScope<AutoAddSignal>.getCallback(): CastController.Callback {
return CastController.Callback {
- val isCasting =
- controller.castDevices.any {
- it.state == CastController.CastDevice.STATE_CONNECTED ||
- it.state == CastController.CastDevice.STATE_CONNECTING
- }
- if (isCasting) {
+ if (controller.castDevices.any { it.isCasting }) {
sendAdd()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 169cdc1..8a72e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -60,7 +60,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel;
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.DialogKt;
@@ -193,14 +193,13 @@
// case where multiple devices were active :-/.
private boolean willPopDialog() {
List<CastDevice> activeDevices = getActiveDevices();
- return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+ return activeDevices.isEmpty() || (activeDevices.get(0).getTag() instanceof RouteInfo);
}
private List<CastDevice> getActiveDevices() {
ArrayList<CastDevice> activeDevices = new ArrayList<>();
for (CastDevice device : mController.getCastDevices()) {
- if (device.state == CastDevice.STATE_CONNECTED
- || device.state == CastDevice.STATE_CONNECTING) {
+ if (device.isCasting()) {
activeDevices.add(device);
}
}
@@ -276,7 +275,7 @@
// We always choose the first device that's in the CONNECTED state in the case where
// multiple devices are CONNECTED at the same time.
for (CastDevice device : devices) {
- if (device.state == CastDevice.STATE_CONNECTED) {
+ if (device.getState() == CastDevice.CastState.Connected) {
state.value = true;
state.secondaryLabel = getDeviceName(device);
state.stateDescription = state.stateDescription + ","
@@ -284,7 +283,7 @@
R.string.accessibility_cast_name, state.label);
connecting = false;
break;
- } else if (device.state == CastDevice.STATE_CONNECTING) {
+ } else if (device.getState() == CastDevice.CastState.Connecting) {
connecting = true;
}
}
@@ -315,7 +314,7 @@
}
private String getDeviceName(CastDevice device) {
- return device.name != null ? device.name
+ return device.getName() != null ? device.getName()
: mContext.getString(R.string.quick_settings_cast_device_default_name);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 70f3b84..a3feb2b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -46,6 +46,7 @@
import com.android.systemui.recordissue.IssueRecordingService
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.recordissue.RecordIssueDialogDelegate
+import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
import com.android.systemui.recordissue.TraceurMessageSender
import com.android.systemui.res.R
import com.android.systemui.screenrecord.RecordingService
@@ -197,8 +198,4 @@
expandedAccessibilityClassName = Switch::class.java.name
}
}
-
- companion object {
- const val TILE_SPEC = "record_issue"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 4715230..284239a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -47,6 +47,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -146,8 +147,9 @@
if (isRecording) {
state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_stop);
} else if (isStarting) {
- // round, since the timer isn't exact
- int countdown = (int) Math.floorDiv(mMillisUntilFinished + 500, 1000);
+ int countdown =
+ (int) ScreenRecordModel.Starting.Companion.toCountdownSeconds(
+ mMillisUntilFinished);
state.secondaryLabel = String.format("%d...", countdown);
} else {
state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_start);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
new file mode 100644
index 0000000..1af328e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.irecording
+
+import android.os.UserHandle
+import com.android.systemui.Flags
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.recordissue.IssueRecordingState
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+
+class IssueRecordingDataInteractor
+@Inject
+constructor(
+ private val state: IssueRecordingState,
+ @Background private val bgCoroutineContext: CoroutineContext,
+) : QSTileDataInteractor<IssueRecordingModel> {
+
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<IssueRecordingModel> =
+ conflatedCallbackFlow {
+ val listener = Runnable { trySend(IssueRecordingModel(state.isRecording)) }
+ state.addListener(listener)
+ awaitClose { state.removeListener(listener) }
+ }
+ .onStart { emit(IssueRecordingModel(state.isRecording)) }
+ .distinctUntilChanged()
+ .flowOn(bgCoroutineContext)
+
+ override fun availability(user: UserHandle): Flow<Boolean> =
+ flowOf(android.os.Build.IS_DEBUGGABLE && Flags.recordIssueQsTile())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
new file mode 100644
index 0000000..ff931b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.irecording
+
+import android.content.res.Resources
+import android.content.res.Resources.Theme
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class IssueRecordingMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Theme,
+) : QSTileDataToStateMapper<IssueRecordingModel> {
+ override fun map(config: QSTileConfig, data: IssueRecordingModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ if (data.isRecording) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ secondaryLabel = resources.getString(R.string.qs_record_issue_stop)
+ icon = { Icon.Resource(R.drawable.qs_record_issue_icon_on, null) }
+ } else {
+ icon = { Icon.Resource(R.drawable.qs_record_issue_icon_off, null) }
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel = resources.getString(R.string.qs_record_issue_start)
+ }
+ supportedActions = setOf(QSTileState.UserAction.CLICK)
+ contentDescription = "$label, $secondaryLabel"
+ }
+}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
similarity index 82%
copy from services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
copy to packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
index bb8db8a..260729b 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamSettingsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-package com.android.server.dreams;
+package com.android.systemui.qs.tiles.impl.irecording
-public class TestDreamSettingsActivity {
-}
+@JvmInline value class IssueRecordingModel(val isRecording: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
new file mode 100644
index 0000000..4971fef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.irecording
+
+import android.app.AlertDialog
+import android.app.BroadcastOptions
+import android.app.PendingIntent
+import android.util.Log
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.recordissue.IssueRecordingService
+import com.android.systemui.recordissue.RecordIssueDialogDelegate
+import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
+import com.android.systemui.screenrecord.RecordingService
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+private const val TAG = "IssueRecordingActionInteractor"
+
+class IssueRecordingUserActionInteractor
+@Inject
+constructor(
+ @Main private val mainCoroutineContext: CoroutineContext,
+ private val keyguardDismissUtil: KeyguardDismissUtil,
+ private val keyguardStateController: KeyguardStateController,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+ private val panelInteractor: PanelInteractor,
+ private val userContextProvider: UserContextProvider,
+ private val delegateFactory: RecordIssueDialogDelegate.Factory,
+) : QSTileUserActionInteractor<IssueRecordingModel> {
+
+ override suspend fun handleInput(input: QSTileInput<IssueRecordingModel>) {
+ if (input.action is QSTileUserAction.Click) {
+ if (input.data.isRecording) {
+ stopIssueRecordingService()
+ } else {
+ withContext(mainCoroutineContext) { showPrompt(input.action.expandable) }
+ }
+ } else {
+ Log.v(TAG, "the RecordIssueTile doesn't handle ${input.action} events yet.")
+ }
+ }
+
+ private fun showPrompt(expandable: Expandable?) {
+ val dialog: AlertDialog =
+ delegateFactory
+ .create {
+ startIssueRecordingService()
+ dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
+ panelInteractor.collapsePanels()
+ }
+ .createDialog()
+ val dismissAction =
+ ActivityStarter.OnDismissAction {
+ // We animate from the touched view only if we are not on the keyguard, given
+ // that if we are we will dismiss it which will also collapse the shade.
+ if (expandable != null && !keyguardStateController.isShowing) {
+ expandable
+ .dialogTransitionController(
+ DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, TILE_SPEC)
+ )
+ ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show()
+ } else {
+ dialog.show()
+ }
+ false
+ }
+ keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true)
+ }
+
+ private fun startIssueRecordingService() =
+ PendingIntent.getForegroundService(
+ userContextProvider.userContext,
+ RecordingService.REQUEST_CODE,
+ IssueRecordingService.getStartIntent(userContextProvider.userContext),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+
+ private fun stopIssueRecordingService() =
+ PendingIntent.getService(
+ userContextProvider.userContext,
+ RecordingService.REQUEST_CODE,
+ IssueRecordingService.getStopIntent(userContextProvider.userContext),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
index 7446708..e74e77f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
@@ -61,7 +61,7 @@
contentDescription = null
)
icon = { loadedIcon }
- val countDown = Math.floorDiv(data.millisUntilStarted + 500, 1000)
+ val countDown = data.countdownSeconds
sideViewIcon = QSTileState.SideViewIcon.None
secondaryLabel = String.format("%d...", countDown)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
index bd748d5..e395d30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
@@ -26,6 +26,8 @@
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
@@ -37,6 +39,7 @@
class QuickSettingsShadeSceneViewModel
@Inject
constructor(
+ shadeInteractor: ShadeInteractor,
val overlayShadeViewModel: OverlayShadeViewModel,
val brightnessSliderViewModel: BrightnessSliderViewModel,
val tileGridViewModel: TileGridViewModel,
@@ -46,7 +49,11 @@
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
MutableStateFlow(
mapOf(
- Swipe.Up to SceneFamilies.Home,
+ if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
+ Swipe.Up
+ } else {
+ Swipe.Down
+ } to SceneFamilies.Home,
Back to SceneFamilies.Home,
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
index 4ea3345..b077349 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
@@ -18,7 +18,7 @@
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.tiles.RecordIssueTile
+import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
@@ -35,11 +35,7 @@
) {
private val prefs =
- userFileManager.getSharedPreferences(
- RecordIssueTile.TILE_SPEC,
- Context.MODE_PRIVATE,
- userTracker.userId
- )
+ userFileManager.getSharedPreferences(TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId)
var takeBugreport
get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
index 26af9a7..907b92c 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
@@ -16,12 +16,20 @@
package com.android.systemui.recordissue
+import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.RecordIssueTile
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingDataInteractor
+import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingMapper
+import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingModel
+import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingUserActionInteractor
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -34,19 +42,19 @@
/** Inject RecordIssueTile into tileMap in QSModule */
@Binds
@IntoMap
- @StringKey(RecordIssueTile.TILE_SPEC)
+ @StringKey(TILE_SPEC)
fun bindRecordIssueTile(recordIssueTile: RecordIssueTile): QSTileImpl<*>
companion object {
- const val RECORD_ISSUE_TILE_SPEC = "record_issue"
+ const val TILE_SPEC = "record_issue"
@Provides
@IntoMap
- @StringKey(RECORD_ISSUE_TILE_SPEC)
+ @StringKey(TILE_SPEC)
fun provideRecordIssueTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
QSTileConfig(
- tileSpec = TileSpec.create(RECORD_ISSUE_TILE_SPEC),
+ tileSpec = TileSpec.create(TILE_SPEC),
uiConfig =
QSTileUIConfig.Resource(
iconRes = R.drawable.qs_record_issue_icon_off,
@@ -54,5 +62,24 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
)
+
+ /** Inject FlashlightTile into tileViewModelMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(TILE_SPEC)
+ fun provideIssueRecordingTileViewModel(
+ factory: QSTileViewModelFactory.Static<IssueRecordingModel>,
+ mapper: IssueRecordingMapper,
+ stateInteractor: IssueRecordingDataInteractor,
+ userActionInteractor: IssueRecordingUserActionInteractor
+ ): QSTileViewModel =
+ if (Flags.qsNewTilesFuture())
+ factory.create(
+ TileSpec.create(TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ else StubQSTileViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 323ca87..6e89973 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -23,8 +23,10 @@
import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule
import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule
+import com.android.systemui.scene.domain.startable.KeyguardStateCallbackStartable
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.domain.startable.ScrimStartable
+import com.android.systemui.scene.domain.startable.StatusBarStartable
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shared.flag.DualShade
@@ -66,6 +68,16 @@
@Binds
@IntoMap
+ @ClassKey(StatusBarStartable::class)
+ fun statusBarStartable(impl: StatusBarStartable): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardStateCallbackStartable::class)
+ fun keyguardStateCallbackStartable(impl: KeyguardStateCallbackStartable): CoreStartable
+
+ @Binds
+ @IntoMap
@ClassKey(WindowRootViewVisibilityInteractor::class)
fun bindWindowRootViewVisibilityInteractor(
impl: WindowRootViewVisibilityInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 4691eba..7d63b4c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -24,8 +24,10 @@
import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule
import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule
+import com.android.systemui.scene.domain.startable.KeyguardStateCallbackStartable
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.domain.startable.ScrimStartable
+import com.android.systemui.scene.domain.startable.StatusBarStartable
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shared.flag.DualShade
@@ -72,6 +74,16 @@
@Binds
@IntoMap
+ @ClassKey(StatusBarStartable::class)
+ fun statusBarStartable(impl: StatusBarStartable): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardStateCallbackStartable::class)
+ fun keyguardStateCallbackStartable(impl: KeyguardStateCallbackStartable): CoreStartable
+
+ @Binds
+ @IntoMap
@ClassKey(WindowRootViewVisibilityInteractor::class)
fun bindWindowRootViewVisibilityInteractor(
impl: WindowRootViewVisibilityInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
index 6bcd923..233e9b5 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
@@ -121,7 +121,7 @@
private val SceneKey.canBeOccluded: Boolean
get() =
when (this) {
- Scenes.Bouncer -> true
+ Scenes.Bouncer -> false
Scenes.Communal -> true
Scenes.Gone -> true
Scenes.Lockscreen -> true
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 4738dbd..25a9e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -22,6 +22,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.resolver.SceneResolver
import com.android.systemui.scene.shared.logger.SceneLogger
@@ -60,6 +61,7 @@
private val logger: SceneLogger,
private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
) {
interface OnSceneAboutToChangeListener {
@@ -381,7 +383,8 @@
val isChangeAllowed =
to != Scenes.Gone ||
inMidTransitionFromGone ||
- deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked
+ deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked ||
+ !keyguardEnabledInteractor.isKeyguardEnabled.value
check(isChangeAllowed) {
"Cannot change to the Gone scene while the device is locked and not currently" +
" transitioning from Gone. Current transition state is ${transitionState.value}." +
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index 9e91b66..41a3c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -22,6 +22,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import dagger.Binds
@@ -45,11 +46,13 @@
constructor(
@Application private val applicationScope: CoroutineScope,
deviceEntryInteractor: DeviceEntryInteractor,
+ keyguardEnabledInteractor: KeyguardEnabledInteractor,
) : SceneResolver {
override val targetFamily: SceneKey = SceneFamilies.Home
override val resolvedScene: StateFlow<SceneKey> =
combine(
+ keyguardEnabledInteractor.isKeyguardEnabled,
deviceEntryInteractor.canSwipeToEnter,
deviceEntryInteractor.isDeviceEntered,
deviceEntryInteractor.isUnlocked,
@@ -60,20 +63,23 @@
started = SharingStarted.Eagerly,
initialValue =
homeScene(
- deviceEntryInteractor.canSwipeToEnter.value,
- deviceEntryInteractor.isDeviceEntered.value,
- deviceEntryInteractor.isUnlocked.value,
+ isKeyguardEnabled = keyguardEnabledInteractor.isKeyguardEnabled.value,
+ canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value,
+ isDeviceEntered = deviceEntryInteractor.isDeviceEntered.value,
+ isUnlocked = deviceEntryInteractor.isUnlocked.value,
)
)
override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes
private fun homeScene(
+ isKeyguardEnabled: Boolean,
canSwipeToEnter: Boolean?,
isDeviceEntered: Boolean,
isUnlocked: Boolean,
): SceneKey =
when {
+ !isKeyguardEnabled -> Scenes.Gone
canSwipeToEnter == true -> Scenes.Lockscreen
!isDeviceEntered -> Scenes.Lockscreen
!isUnlocked -> Scenes.Lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt
new file mode 100644
index 0000000..6d1c1a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.scene.domain.startable
+
+import android.os.DeadObjectException
+import android.os.RemoteException
+import com.android.internal.policy.IKeyguardStateCallback
+import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.TrustInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/** Keeps all [IKeyguardStateCallback]s hydrated with the latest state. */
+@SysUISingleton
+class KeyguardStateCallbackStartable
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val sceneInteractor: SceneInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val simBouncerInteractor: SimBouncerInteractor,
+ private val trustInteractor: TrustInteractor,
+) : CoreStartable {
+
+ private val callbacks = mutableListOf<IKeyguardStateCallback>()
+
+ override fun start() {
+ if (!SceneContainerFlag.isEnabled) {
+ return
+ }
+
+ hydrateKeyguardShowingAndInputRestrictionStates()
+ hydrateSimSecureState()
+ notifyWhenKeyguardShowingChanged()
+ notifyWhenTrustChanged()
+ }
+
+ fun addCallback(callback: IKeyguardStateCallback) {
+ SceneContainerFlag.assertInNewMode()
+
+ callbacks.add(callback)
+
+ applicationScope.launch(backgroundDispatcher) {
+ callback.onShowingStateChanged(
+ !deviceEntryInteractor.isDeviceEntered.value,
+ selectedUserInteractor.getSelectedUserId(),
+ )
+ callback.onTrustedChanged(trustInteractor.isTrusted.value)
+ callback.onSimSecureStateChanged(simBouncerInteractor.isAnySimSecure.value)
+ // TODO(b/348644111): add support for mNeedToReshowWhenReenabled
+ callback.onInputRestrictedStateChanged(!deviceEntryInteractor.isDeviceEntered.value)
+ }
+ }
+
+ private fun hydrateKeyguardShowingAndInputRestrictionStates() {
+ applicationScope.launch {
+ combine(
+ selectedUserInteractor.selectedUser,
+ deviceEntryInteractor.isDeviceEntered,
+ ::Pair
+ )
+ .collectLatest { (selectedUserId, isDeviceEntered) ->
+ val iterator = callbacks.iterator()
+ withContext(backgroundDispatcher) {
+ while (iterator.hasNext()) {
+ val callback = iterator.next()
+ try {
+ callback.onShowingStateChanged(!isDeviceEntered, selectedUserId)
+ // TODO(b/348644111): add support for mNeedToReshowWhenReenabled
+ callback.onInputRestrictedStateChanged(!isDeviceEntered)
+ } catch (e: RemoteException) {
+ if (e is DeadObjectException) {
+ iterator.remove()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun hydrateSimSecureState() {
+ applicationScope.launch {
+ simBouncerInteractor.isAnySimSecure.collectLatest { isSimSecured ->
+ val iterator = callbacks.iterator()
+ withContext(backgroundDispatcher) {
+ while (iterator.hasNext()) {
+ val callback = iterator.next()
+ try {
+ callback.onSimSecureStateChanged(isSimSecured)
+ } catch (e: RemoteException) {
+ if (e is DeadObjectException) {
+ iterator.remove()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun notifyWhenKeyguardShowingChanged() {
+ applicationScope.launch {
+ // This is equivalent to isDeviceEntered but it waits for the full transition animation
+ // to finish before emitting a new value and not just for the current scene to be
+ // switched.
+ sceneInteractor.transitionState
+ .filter { it.isIdle(Scenes.Gone) || it.isIdle(Scenes.Lockscreen) }
+ .map { it.isIdle(Scenes.Lockscreen) }
+ .distinctUntilChanged()
+ .collectLatest { trustInteractor.reportKeyguardShowingChanged() }
+ }
+ }
+
+ private fun notifyWhenTrustChanged() {
+ applicationScope.launch {
+ trustInteractor.isTrusted.collectLatest { isTrusted ->
+ val iterator = callbacks.iterator()
+ withContext(backgroundDispatcher) {
+ while (iterator.hasNext()) {
+ val callback = iterator.next()
+ try {
+ callback.onTrustedChanged(isTrusted)
+ } catch (e: RemoteException) {
+ if (e is DeadObjectException) {
+ iterator.remove()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 1e689bd..1f4820a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -38,7 +38,10 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SysUiState
@@ -82,6 +85,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -104,6 +108,7 @@
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
private val bouncerInteractor: BouncerInteractor,
private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val sysUiState: SysUiState,
@DisplayId private val displayId: Int,
private val sceneLogger: SceneLogger,
@@ -123,6 +128,8 @@
private val sceneBackInteractor: SceneBackInteractor,
private val shadeSessionStorage: SessionStorage,
private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
+ private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
@@ -140,6 +147,8 @@
hydrateWindowController()
hydrateBackStack()
resetShadeSessions()
+ handleKeyguardEnabledness()
+ notifyKeyguardDismissCallbacks()
} else {
sceneLogger.logFrameworkEnabled(
isEnabled = false,
@@ -413,9 +422,9 @@
powerInteractor.isAsleep.collect { isAsleep ->
if (isAsleep) {
switchToScene(
- // TODO(b/336581871): add sceneState?
targetSceneKey = Scenes.Lockscreen,
loggingReason = "device is starting to sleep",
+ sceneState = keyguardTransitionInteractor.asleepKeyguardState.value,
)
} else {
val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
@@ -650,10 +659,60 @@
}
}
- private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
+ private fun handleKeyguardEnabledness() {
+ // Automatically switches scenes when keyguard is enabled or disabled, as needed.
+ applicationScope.launch {
+ keyguardEnabledInteractor.isKeyguardEnabled
+ .sample(
+ combine(
+ deviceEntryInteractor.isInLockdown,
+ deviceEntryInteractor.isDeviceEntered,
+ ::Pair,
+ )
+ ) { isKeyguardEnabled, (isInLockdown, isDeviceEntered) ->
+ when {
+ !isKeyguardEnabled && !isInLockdown && !isDeviceEntered -> {
+ keyguardEnabledInteractor.setShowKeyguardWhenReenabled(true)
+ Scenes.Gone to "Keyguard became disabled"
+ }
+ isKeyguardEnabled &&
+ keyguardEnabledInteractor.isShowKeyguardWhenReenabled() -> {
+ keyguardEnabledInteractor.setShowKeyguardWhenReenabled(false)
+ Scenes.Lockscreen to "Keyguard became enabled"
+ }
+ else -> null
+ }
+ }
+ .filterNotNull()
+ .collect { (targetScene, loggingReason) ->
+ switchToScene(targetScene, loggingReason)
+ }
+ }
+
+ // Clears the showKeyguardWhenReenabled if the auth method changes to an insecure one.
+ applicationScope.launch {
+ authenticationInteractor
+ .get()
+ .authenticationMethod
+ .map { it.isSecure }
+ .distinctUntilChanged()
+ .collect { isAuthenticationMethodSecure ->
+ if (!isAuthenticationMethodSecure) {
+ keyguardEnabledInteractor.setShowKeyguardWhenReenabled(false)
+ }
+ }
+ }
+ }
+
+ private fun switchToScene(
+ targetSceneKey: SceneKey,
+ loggingReason: String,
+ sceneState: Any? = null
+ ) {
sceneInteractor.changeScene(
toScene = targetSceneKey,
loggingReason = loggingReason,
+ sceneState = sceneState,
)
}
@@ -664,4 +723,16 @@
}
}
}
+
+ private fun notifyKeyguardDismissCallbacks() {
+ applicationScope.launch {
+ sceneInteractor.currentScene.pairwise().collect { (from, to) ->
+ when {
+ from != Scenes.Bouncer -> Unit
+ to == Scenes.Gone -> dismissCallbackRegistry.notifyDismissSucceeded()
+ else -> dismissCallbackRegistry.notifyDismissCancelled()
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
new file mode 100644
index 0000000..893f030
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
@@ -0,0 +1,180 @@
+/*
+ * 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.scene.domain.startable
+
+import android.annotation.SuppressLint
+import android.app.StatusBarManager
+import android.content.Context
+import android.os.Binder
+import android.os.IBinder
+import android.os.RemoteException
+import android.provider.DeviceConfig
+import android.util.Log
+import com.android.compose.animation.scene.SceneKey
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.CoreStartable
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.deviceconfig.domain.interactor.DeviceConfigInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.navigation.domain.interactor.NavigationInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessModel
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+class StatusBarStartable
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ @Application private val applicationContext: Context,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val sceneInteractor: SceneInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
+ private val deviceConfigInteractor: DeviceConfigInteractor,
+ private val navigationInteractor: NavigationInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
+ private val powerInteractor: PowerInteractor,
+ private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+ private val statusBarService: IStatusBarService,
+) : CoreStartable {
+
+ private val disableToken: IBinder = Binder()
+
+ override fun start() {
+ if (!SceneContainerFlag.isEnabled) {
+ return
+ }
+
+ applicationScope.launch {
+ combine(
+ selectedUserInteractor.selectedUser,
+ sceneInteractor.currentScene,
+ deviceEntryInteractor.isDeviceEntered,
+ sceneContainerOcclusionInteractor.invisibleDueToOcclusion,
+ deviceConfigInteractor.property(
+ namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
+ name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+ default = true,
+ ),
+ navigationInteractor.isGesturalMode,
+ authenticationInteractor.authenticationMethod,
+ powerInteractor.detailedWakefulness,
+ ) { values ->
+ val selectedUserId = values[0] as Int
+ val currentScene = values[1] as SceneKey
+ val isDeviceEntered = values[2] as Boolean
+ val isOccluded = values[3] as Boolean
+ val isShowHomeOverLockscreen = values[4] as Boolean
+ val isGesturalMode = values[5] as Boolean
+ val authenticationMethod = values[6] as AuthenticationMethodModel
+ val wakefulnessModel = values[7] as WakefulnessModel
+
+ val isForceHideHomeAndRecents = currentScene == Scenes.Bouncer
+ val isKeyguardShowing = !isDeviceEntered
+ val isPowerGestureIntercepted =
+ with(wakefulnessModel) {
+ isAwake() &&
+ powerButtonLaunchGestureTriggered &&
+ lastSleepReason == WakeSleepReason.POWER_BUTTON
+ }
+
+ var flags = StatusBarManager.DISABLE_NONE
+
+ if (isForceHideHomeAndRecents || (isKeyguardShowing && !isOccluded)) {
+ if (!isShowHomeOverLockscreen || !isGesturalMode) {
+ flags = flags or StatusBarManager.DISABLE_HOME
+ }
+ flags = flags or StatusBarManager.DISABLE_RECENT
+ }
+
+ if (
+ isPowerGestureIntercepted &&
+ isOccluded &&
+ authenticationMethod.isSecure &&
+ deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+ ) {
+ flags = flags or StatusBarManager.DISABLE_RECENT
+ }
+
+ selectedUserId to flags
+ }
+ .distinctUntilChanged()
+ .collect { (selectedUserId, flags) ->
+ @SuppressLint("WrongConstant", "NonInjectedService")
+ if (applicationContext.getSystemService(Context.STATUS_BAR_SERVICE) == null) {
+ Log.w(TAG, "Could not get status bar manager")
+ return@collect
+ }
+
+ withContext(backgroundDispatcher) {
+ try {
+ statusBarService.disableForUser(
+ flags,
+ disableToken,
+ applicationContext.packageName,
+ selectedUserId,
+ )
+ } catch (e: RemoteException) {
+ Log.d(TAG, "Failed to set disable flags: $flags", e)
+ }
+ }
+ }
+ }
+ }
+
+ override fun onBootCompleted() {
+ applicationScope.launch(backgroundDispatcher) {
+ try {
+ statusBarService.disableForUser(
+ StatusBarManager.DISABLE_NONE,
+ disableToken,
+ applicationContext.packageName,
+ selectedUserInteractor.getSelectedUserId(true),
+ )
+ } catch (e: RemoteException) {
+ Log.d(TAG, "Failed to clear flags", e)
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "StatusBarStartable"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index cf33c4a..6c63c97 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -39,13 +39,14 @@
inline val isEnabled
get() =
sceneContainer() && // mainAconfigFlag
- ComposeLockscreen.isEnabled &&
+ ComposeLockscreen.isEnabled &&
KeyguardBottomAreaRefactor.isEnabled &&
KeyguardWmStateRefactor.isEnabled &&
MigrateClocksToBlueprint.isEnabled &&
NotificationsHeadsUpRefactor.isEnabled &&
PredictiveBackSysUiFlag.isEnabled &&
DeviceEntryUdfpsRefactor.isEnabled
+
// NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
/** The main aconfig flag. */
@@ -91,6 +92,14 @@
@JvmStatic
inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, DESCRIPTION)
+ /**
+ * Called to ensure the new code is only run when the flag is enabled. This will throw an
+ * exception if the flag is disabled to ensure that the refactor author catches issues in
+ * testing.
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, DESCRIPTION)
+
/** Returns a developer-readable string that describes the current requirement list. */
@JvmStatic
fun requirementDescription(): String {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
index ef393e4..be95441 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
@@ -31,6 +31,11 @@
val CollapseShadeInstantly = TransitionKey("CollapseShadeInstantly")
/**
+ * Reference to a scene transition that brings up the shade from the bottom instead of the top.
+ */
+ val OpenBottomShade = TransitionKey("OpenBottomShade")
+
+ /**
* Reference to a scene transition that can collapse the shade scene slightly faster than a
* normal collapse would.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
index c34a6cd..9f48ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene.ui.viewmodel
+import androidx.compose.ui.Alignment
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
@@ -24,6 +25,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
@@ -39,7 +41,7 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- shadeInteractor: ShadeInteractor,
+ private val shadeInteractor: ShadeInteractor,
) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
shadeInteractor.shadeMode
@@ -62,23 +64,38 @@
// TODO(b/338577208): Remove this once we add Dual Shade invocation zones.
shadeMode is ShadeMode.Dual
) {
- put(
- Swipe(
- pointerCount = 2,
- fromSource = Edge.Top,
- direction = SwipeDirection.Down,
- ),
- UserActionResult(SceneFamilies.QuickSettings)
- )
+ if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
+ put(
+ Swipe(
+ pointerCount = 2,
+ fromSource = Edge.Bottom,
+ direction = SwipeDirection.Up,
+ ),
+ UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade)
+ )
+ } else {
+ put(
+ Swipe(
+ pointerCount = 2,
+ fromSource = Edge.Top,
+ direction = SwipeDirection.Down,
+ ),
+ UserActionResult(SceneFamilies.QuickSettings)
+ )
+ }
}
- put(
- Swipe(direction = SwipeDirection.Down),
- UserActionResult(
- SceneFamilies.NotifShade,
- ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+ if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
+ put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade))
+ } else {
+ put(
+ Swipe.Down,
+ UserActionResult(
+ SceneFamilies.NotifShade,
+ ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+ )
)
- )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/model/ScreenRecordModel.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/model/ScreenRecordModel.kt
index b225444..ada5d8c0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/model/ScreenRecordModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/model/ScreenRecordModel.kt
@@ -22,7 +22,17 @@
data object Recording : ScreenRecordModel
/** A screen recording will begin in [millisUntilStarted] ms. */
- data class Starting(val millisUntilStarted: Long) : ScreenRecordModel
+ data class Starting(val millisUntilStarted: Long) : ScreenRecordModel {
+ val countdownSeconds = millisUntilStarted.toCountdownSeconds()
+
+ companion object {
+ /**
+ * Returns the number of seconds until screen recording will start, used to show a 3-2-1
+ * countdown.
+ */
+ fun Long.toCountdownSeconds() = Math.floorDiv(this + 500, 1000)
+ }
+ }
/** There's nothing related to screen recording happening. */
data object DoingNothing : ScreenRecordModel
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt b/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt
new file mode 100644
index 0000000..6730d2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/HeadlessScreenshotHandler.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.screenshot
+
+import android.net.Uri
+import android.os.UserManager
+import android.util.Log
+import android.view.WindowManager
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.UUID
+import java.util.concurrent.Executor
+import java.util.concurrent.Executors
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * A ScreenshotHandler that just saves the screenshot and calls back as appropriate, with no UI.
+ *
+ * Basically, ScreenshotController with all the UI bits ripped out.
+ */
+class HeadlessScreenshotHandler
+@Inject
+constructor(
+ private val imageExporter: ImageExporter,
+ @Main private val mainExecutor: Executor,
+ private val imageCapture: ImageCapture,
+ private val userManager: UserManager,
+ private val uiEventLogger: UiEventLogger,
+ private val notificationsControllerFactory: ScreenshotNotificationsController.Factory,
+) : ScreenshotHandler {
+
+ override fun handleScreenshot(
+ screenshot: ScreenshotData,
+ finisher: Consumer<Uri?>,
+ requestCallback: TakeScreenshotService.RequestCallback
+ ) {
+ if (screenshot.type == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) {
+ screenshot.bitmap = imageCapture.captureDisplay(screenshot.displayId, crop = null)
+ }
+
+ if (screenshot.bitmap == null) {
+ Log.e(TAG, "handleScreenshot: Screenshot bitmap was null")
+ notificationsControllerFactory
+ .create(screenshot.displayId)
+ .notifyScreenshotError(R.string.screenshot_failed_to_capture_text)
+ requestCallback.reportError()
+ return
+ }
+
+ val future: ListenableFuture<ImageExporter.Result> =
+ imageExporter.export(
+ Executors.newSingleThreadExecutor(),
+ UUID.randomUUID(),
+ screenshot.bitmap,
+ screenshot.getUserOrDefault(),
+ screenshot.displayId
+ )
+ future.addListener(
+ {
+ try {
+ val result = future.get()
+ Log.d(TAG, "Saved screenshot: $result")
+ logScreenshotResultStatus(result.uri, screenshot)
+ finisher.accept(result.uri)
+ requestCallback.onFinish()
+ } catch (e: Exception) {
+ Log.d(TAG, "Failed to store screenshot", e)
+ finisher.accept(null)
+ requestCallback.reportError()
+ }
+ },
+ mainExecutor
+ )
+ }
+
+ private fun logScreenshotResultStatus(uri: Uri?, screenshot: ScreenshotData) {
+ if (uri == null) {
+ uiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, screenshot.packageNameString)
+ notificationsControllerFactory
+ .create(screenshot.displayId)
+ .notifyScreenshotError(R.string.screenshot_failed_to_save_text)
+ } else {
+ uiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, screenshot.packageNameString)
+ if (userManager.isManagedProfile(screenshot.getUserOrDefault().identifier)) {
+ uiEventLogger.log(
+ ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE,
+ 0,
+ screenshot.packageNameString
+ )
+ }
+ }
+ }
+
+ companion object {
+ const val TAG = "HeadlessScreenshotHandler"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
index 2ffb783..5f16886 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsController.kt
@@ -18,6 +18,7 @@
import android.app.assist.AssistContent
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -84,9 +85,9 @@
}
inner class ActionsCallback(private val screenshotId: UUID) {
- fun providePreviewAction(onClick: () -> Unit) {
+ fun providePreviewAction(previewAction: PreviewAction) {
if (screenshotId == currentScreenshotId) {
- viewModel.setPreviewAction(onClick)
+ viewModel.setPreviewAction(previewAction)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index b8029c8..c216f1d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -29,6 +29,7 @@
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -40,7 +41,9 @@
*/
interface ScreenshotActionsProvider {
fun onScrollChipReady(onClick: Runnable)
+
fun onScrollChipInvalidated()
+
fun setCompletedScreenshot(result: ScreenshotSavedResult)
/**
@@ -75,17 +78,19 @@
private var result: ScreenshotSavedResult? = null
init {
- actionsCallback.providePreviewAction {
- debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" }
- uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString)
- onDeferrableActionTapped { result ->
- actionExecutor.startSharedTransition(
- createEdit(result.uri, context),
- result.user,
- true
- )
+ actionsCallback.providePreviewAction(
+ PreviewAction(context.resources.getString(R.string.screenshot_edit_description)) {
+ debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" }
+ uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString)
+ onDeferrableActionTapped { result ->
+ actionExecutor.startSharedTransition(
+ createEdit(result.uri, context),
+ result.user,
+ true
+ )
+ }
}
- }
+ )
actionsCallback.provideActionButton(
ActionButtonAppearance(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index e8dfac8..c87b1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -101,7 +101,7 @@
/**
* Controls the state and flow for screenshots.
*/
-public class ScreenshotController {
+public class ScreenshotController implements ScreenshotHandler {
private static final String TAG = logTag(ScreenshotController.class);
/**
@@ -351,7 +351,8 @@
mShowUIOnExternalDisplay = showUIOnExternalDisplay;
}
- void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
+ @Override
+ public void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
RequestCallback requestCallback) {
Assert.isMainThread();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 3c3797b..2699657 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.screenshot
import android.net.Uri
@@ -7,12 +23,12 @@
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import com.android.internal.logging.UiEventLogger
import com.android.internal.util.ScreenshotRequest
-import com.android.systemui.Flags.screenshotShelfUi2
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.res.R
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
import java.util.function.Consumer
import javax.inject.Inject
@@ -26,9 +42,13 @@
onSaved: (Uri?) -> Unit,
requestCallback: RequestCallback
)
+
fun onCloseSystemDialogsReceived()
+
fun removeWindows()
+
fun onDestroy()
+
fun executeScreenshotsAsync(
screenshotRequest: ScreenshotRequest,
onSaved: Consumer<Uri?>,
@@ -36,6 +56,14 @@
)
}
+interface ScreenshotHandler {
+ fun handleScreenshot(
+ screenshot: ScreenshotData,
+ finisher: Consumer<Uri?>,
+ requestCallback: RequestCallback
+ )
+}
+
/**
* Receives the signal to take a screenshot from [TakeScreenshotService], and calls back with the
* result.
@@ -52,10 +80,10 @@
private val screenshotRequestProcessor: ScreenshotRequestProcessor,
private val uiEventLogger: UiEventLogger,
private val screenshotNotificationControllerFactory: ScreenshotNotificationsController.Factory,
+ private val headlessScreenshotHandler: HeadlessScreenshotHandler,
) : TakeScreenshotExecutor {
-
private val displays = displayRepository.displays
- private val screenshotControllers = mutableMapOf<Int, ScreenshotController>()
+ private var screenshotController: ScreenshotController? = null
private val notificationControllers = mutableMapOf<Int, ScreenshotNotificationsController>()
/**
@@ -73,9 +101,15 @@
val resultCallbackWrapper = MultiResultCallbackWrapper(requestCallback)
displays.forEach { display ->
val displayId = display.displayId
+ var screenshotHandler: ScreenshotHandler =
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ getScreenshotController(display)
+ } else {
+ headlessScreenshotHandler
+ }
Log.d(TAG, "Executing screenshot for display $displayId")
dispatchToController(
- display = display,
+ screenshotHandler,
rawScreenshotData = ScreenshotData.fromRequest(screenshotRequest, displayId),
onSaved =
if (displayId == Display.DEFAULT_DISPLAY) {
@@ -88,7 +122,7 @@
/** All logging should be triggered only by this method. */
private suspend fun dispatchToController(
- display: Display,
+ screenshotHandler: ScreenshotHandler,
rawScreenshotData: ScreenshotData,
onSaved: (Uri?) -> Unit,
callback: RequestCallback
@@ -102,13 +136,12 @@
logScreenshotRequested(rawScreenshotData)
onFailedScreenshotRequest(rawScreenshotData, callback)
}
- .getOrNull()
- ?: return
+ .getOrNull() ?: return
logScreenshotRequested(screenshotData)
Log.d(TAG, "Screenshot request: $screenshotData")
try {
- getScreenshotController(display).handleScreenshot(screenshotData, onSaved, callback)
+ screenshotHandler.handleScreenshot(screenshotData, onSaved, callback)
} catch (e: IllegalStateException) {
Log.e(TAG, "Error while ScreenshotController was handling ScreenshotData!", e)
onFailedScreenshotRequest(screenshotData, callback)
@@ -140,44 +173,32 @@
private suspend fun getDisplaysToScreenshot(requestType: Int): List<Display> {
val allDisplays = displays.first()
- return if (requestType == TAKE_SCREENSHOT_PROVIDED_IMAGE || screenshotShelfUi2()) {
- // If this is a provided image or using the shelf UI, just screenshot th default display
+ return if (requestType == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ // If this is a provided image just screenshot th default display
allDisplays.filter { it.displayId == Display.DEFAULT_DISPLAY }
} else {
allDisplays.filter { it.type in ALLOWED_DISPLAY_TYPES }
}
}
- /** Propagates the close system dialog signal to all controllers. */
+ /** Propagates the close system dialog signal to the ScreenshotController. */
override fun onCloseSystemDialogsReceived() {
- screenshotControllers.forEach { (_, screenshotController) ->
- if (!screenshotController.isPendingSharedTransition) {
- screenshotController.requestDismissal(ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER)
- }
+ if (screenshotController?.isPendingSharedTransition == false) {
+ screenshotController?.requestDismissal(SCREENSHOT_DISMISSED_OTHER)
}
}
/** Removes all screenshot related windows. */
override fun removeWindows() {
- screenshotControllers.forEach { (_, screenshotController) ->
- screenshotController.removeWindow()
- }
+ screenshotController?.removeWindow()
}
/**
* Destroys the executor. Afterwards, this class is not expected to work as intended anymore.
*/
override fun onDestroy() {
- screenshotControllers.forEach { (_, screenshotController) ->
- screenshotController.onDestroy()
- }
- screenshotControllers.clear()
- }
-
- private fun getScreenshotController(display: Display): ScreenshotController {
- return screenshotControllers.computeIfAbsent(display.displayId) {
- screenshotControllerFactory.create(display, /* showUIOnExternalDisplay= */ false)
- }
+ screenshotController?.onDestroy()
+ screenshotController = null
}
private fun getNotificationController(id: Int): ScreenshotNotificationsController {
@@ -197,6 +218,12 @@
}
}
+ private fun getScreenshotController(display: Display): ScreenshotController {
+ val controller = screenshotController ?: screenshotControllerFactory.create(display, false)
+ screenshotController = controller
+ return controller
+ }
+
/**
* Returns a [RequestCallback] that wraps [originalCallback].
*
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index d87d85b..ab4480d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -16,10 +16,13 @@
package com.android.systemui.screenshot.appclips;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_TASK_ID;
import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER;
import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.PERMISSION_SELF;
@@ -42,19 +45,27 @@
import android.util.Log;
import android.view.View;
import android.widget.Button;
+import android.widget.CheckBox;
import android.widget.ImageView;
+import android.widget.TextView;
import androidx.activity.ComponentActivity;
import androidx.annotation.Nullable;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.ViewModelProvider;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLogger.UiEventEnum;
import com.android.settingslib.Utils;
+import com.android.systemui.Flags;
import com.android.systemui.res.R;
import com.android.systemui.screenshot.scroll.CropView;
import com.android.systemui.settings.UserTracker;
+import java.util.Set;
+
import javax.inject.Inject;
/**
@@ -73,8 +84,6 @@
*
* <p>This {@link Activity} runs in its own separate process to isolate memory intensive image
* editing from SysUI process.
- *
- * TODO(b/267309532): Polish UI and animations.
*/
public class AppClipsActivity extends ComponentActivity {
@@ -94,6 +103,8 @@
private CropView mCropView;
private Button mSave;
private Button mCancel;
+ private CheckBox mBacklinksIncludeDataCheckBox;
+ private TextView mBacklinksDataTextView;
private AppClipsViewModel mViewModel;
private ResultReceiver mResultReceiver;
@@ -149,26 +160,47 @@
mLayout = getLayoutInflater().inflate(R.layout.app_clips_screenshot, null);
mRoot = mLayout.findViewById(R.id.root);
+ // Manually handle window insets post Android V to support edge-to-edge display.
+ ViewCompat.setOnApplyWindowInsetsListener(mRoot, (v, windowInsets) -> {
+ Insets insets = windowInsets.getInsets(
+ WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return WindowInsetsCompat.CONSUMED;
+ });
+
mSave = mLayout.findViewById(R.id.save);
mCancel = mLayout.findViewById(R.id.cancel);
mSave.setOnClickListener(this::onClick);
mCancel.setOnClickListener(this::onClick);
-
-
mCropView = mLayout.findViewById(R.id.crop_view);
-
mPreview = mLayout.findViewById(R.id.preview);
mPreview.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
updateImageDimensions());
+ mBacklinksDataTextView = mLayout.findViewById(R.id.backlinks_data);
+ mBacklinksIncludeDataCheckBox = mLayout.findViewById(R.id.backlinks_include_data);
+ mBacklinksIncludeDataCheckBox.setOnCheckedChangeListener(
+ (buttonView, isChecked) ->
+ mBacklinksDataTextView.setVisibility(isChecked ? View.VISIBLE : View.GONE));
+
mViewModel = new ViewModelProvider(this, mViewModelFactory).get(AppClipsViewModel.class);
mViewModel.getScreenshot().observe(this, this::setScreenshot);
mViewModel.getResultLiveData().observe(this, this::setResultThenFinish);
mViewModel.getErrorLiveData().observe(this, this::setErrorThenFinish);
+ mViewModel.getBacklinksLiveData().observe(this, this::setBacklinksData);
if (savedInstanceState == null) {
- mViewModel.performScreenshot();
+ int displayId = getDisplayId();
+ mViewModel.performScreenshot(displayId);
+
+ if (Flags.appClipsBacklinks()) {
+ int appClipsTaskId = getTaskId();
+ int callingPackageTaskId = intent.getIntExtra(EXTRA_CALLING_PACKAGE_TASK_ID,
+ INVALID_TASK_ID);
+ Set<Integer> taskIdsToIgnore = Set.of(appClipsTaskId, callingPackageTaskId);
+ mViewModel.triggerBacklinks(taskIdsToIgnore, displayId);
+ }
}
}
@@ -217,6 +249,9 @@
// Screenshot is now available so set content view.
setContentView(mLayout);
+
+ // Request view to apply insets as it is added late and not when activity was first created.
+ mRoot.requestApplyInsets();
}
private void onClick(View view) {
@@ -268,7 +303,7 @@
mResultReceiver.send(Activity.RESULT_OK, data);
logUiEvent(SCREENSHOT_FOR_NOTE_ACCEPTED);
} catch (Exception e) {
- // Do nothing.
+ Log.e(TAG, "Error while returning data to trampoline activity", e);
}
// Nullify the ResultReceiver before finishing to avoid resending the result.
@@ -281,6 +316,20 @@
finish();
}
+ private void setBacklinksData(InternalBacklinksData backlinksData) {
+ mBacklinksIncludeDataCheckBox.setVisibility(View.VISIBLE);
+ mBacklinksDataTextView.setVisibility(
+ mBacklinksIncludeDataCheckBox.isChecked() ? View.VISIBLE : View.GONE);
+
+ mBacklinksDataTextView.setText(backlinksData.getClipData().getDescription().getLabel());
+
+ Drawable appIcon = backlinksData.getAppIcon();
+ int size = getResources().getDimensionPixelSize(R.dimen.appclips_backlinks_icon_size);
+ appIcon.setBounds(/* left= */ 0, /* top= */ 0, /* right= */ size, /* bottom= */ size);
+ mBacklinksDataTextView.setCompoundDrawablesRelative(/* start= */ appIcon, /* top= */
+ null, /* end= */ null, /* bottom= */ null);
+ }
+
private void setError(int errorCode) {
if (mResultReceiver == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
index 7de22b1..aaa5dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.UserHandle;
+import android.util.Log;
import androidx.annotation.Nullable;
@@ -27,19 +28,18 @@
import com.android.internal.infra.ServiceConnector;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.settings.DisplayTracker;
import javax.inject.Inject;
/** An intermediary singleton object to help communicating with the cross process service. */
@SysUISingleton
class AppClipsCrossProcessHelper {
+ private static final String TAG = AppClipsCrossProcessHelper.class.getSimpleName();
private final ServiceConnector<IAppClipsScreenshotHelperService> mProxyConnector;
- private final DisplayTracker mDisplayTracker;
@Inject
- AppClipsCrossProcessHelper(@Application Context context, DisplayTracker displayTracker) {
+ AppClipsCrossProcessHelper(@Application Context context) {
// Start a service as main user so that even if the app clips activity is running as work
// profile user the service is able to use correct instance of Bubbles to grab a screenshot
// excluding the bubble layer.
@@ -48,7 +48,6 @@
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
| Context.BIND_NOT_VISIBLE, UserHandle.USER_SYSTEM,
IAppClipsScreenshotHelperService.Stub::asInterface);
- mDisplayTracker = displayTracker;
}
/**
@@ -58,15 +57,16 @@
* pass around but not a {@link Bitmap}.
*/
@Nullable
- Bitmap takeScreenshot() {
+ Bitmap takeScreenshot(int displayId) {
try {
AndroidFuture<ScreenshotHardwareBufferInternal> future =
mProxyConnector.postForResult(
- service ->
- // Take a screenshot of the default display of the user.
- service.takeScreenshot(mDisplayTracker.getDefaultDisplayId()));
+ service -> service.takeScreenshot(displayId));
return future.get().createBitmapThenCloseBuffer();
} catch (Exception e) {
+ Log.e(TAG,
+ String.format("Error while capturing a screenshot of displayId %d", displayId),
+ e);
return null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
index 48449b3..3c4469d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
@@ -85,6 +85,7 @@
static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE";
static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER";
static final String EXTRA_CALLING_PACKAGE_NAME = TAG + "CALLING_PACKAGE_NAME";
+ static final String EXTRA_CALLING_PACKAGE_TASK_ID = TAG + "CALLING_PACKAGE_TASK_ID";
private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
private final NoteTaskController mNoteTaskController;
@@ -193,12 +194,14 @@
ComponentName componentName = ComponentName.unflattenFromString(
getString(R.string.config_screenshotAppClipsActivityComponent));
String callingPackageName = getCallingPackage();
+ int callingPackageTaskId = getTaskId();
Intent intent = new Intent()
.setComponent(componentName)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver)
- .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName);
+ .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName)
+ .putExtra(EXTRA_CALLING_PACKAGE_TASK_ID, callingPackageTaskId);
try {
startActivity(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index 630d338..d30d518 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -16,9 +16,23 @@
package com.android.systemui.screenshot.appclips;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.ACTION_VIEW;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static com.google.common.util.concurrent.Futures.withTimeout;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+
+import android.app.ActivityTaskManager.RootTaskInfo;
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.app.assist.AssistContent;
+import android.content.ClipData;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
import android.graphics.RecordingCanvas;
@@ -26,10 +40,13 @@
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.Log;
import android.view.Display;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
@@ -37,22 +54,36 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.screenshot.AssistContentRequester;
import com.android.systemui.screenshot.ImageExporter;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
/** A {@link ViewModel} to help with the App Clips screenshot flow. */
final class AppClipsViewModel extends ViewModel {
+ private static final String TAG = AppClipsViewModel.class.getSimpleName();
+
private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
private final ImageExporter mImageExporter;
+ private final IActivityTaskManager mAtmService;
+ private final AssistContentRequester mAssistContentRequester;
+ private final PackageManager mPackageManager;
+
@Main
private final Executor mMainExecutor;
@Background
@@ -61,24 +92,34 @@
private final MutableLiveData<Bitmap> mScreenshotLiveData;
private final MutableLiveData<Uri> mResultLiveData;
private final MutableLiveData<Integer> mErrorLiveData;
+ private final MutableLiveData<InternalBacklinksData> mBacklinksLiveData;
- AppClipsViewModel(AppClipsCrossProcessHelper appClipsCrossProcessHelper,
- ImageExporter imageExporter, @Main Executor mainExecutor,
- @Background Executor bgExecutor) {
+ private AppClipsViewModel(AppClipsCrossProcessHelper appClipsCrossProcessHelper,
+ ImageExporter imageExporter, IActivityTaskManager atmService,
+ AssistContentRequester assistContentRequester, PackageManager packageManager,
+ @Main Executor mainExecutor, @Background Executor bgExecutor) {
mAppClipsCrossProcessHelper = appClipsCrossProcessHelper;
mImageExporter = imageExporter;
+ mAtmService = atmService;
+ mAssistContentRequester = assistContentRequester;
+ mPackageManager = packageManager;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
mScreenshotLiveData = new MutableLiveData<>();
mResultLiveData = new MutableLiveData<>();
mErrorLiveData = new MutableLiveData<>();
+ mBacklinksLiveData = new MutableLiveData<>();
}
- /** Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link LiveData}. */
- void performScreenshot() {
+ /**
+ * Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link #getScreenshot()}.
+ *
+ * @param displayId id of the {@link Display} to capture screenshot.
+ */
+ void performScreenshot(int displayId) {
mBgExecutor.execute(() -> {
- Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot();
+ Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot(displayId);
mMainExecutor.execute(() -> {
if (screenshot == null) {
mErrorLiveData.setValue(CAPTURE_CONTENT_FOR_NOTE_FAILED);
@@ -89,6 +130,39 @@
});
}
+ /**
+ * Triggers the Backlinks flow which:
+ * <ul>
+ * <li>Evaluates the task to query.
+ * <li>Requests {@link AssistContent} from that task.
+ * <li>Transforms the {@link AssistContent} into {@link ClipData} for Backlinks.
+ * <li>The {@link ClipData} is reported to activity via {@link #getBacklinksLiveData()}.
+ * </ul>
+ *
+ * @param taskIdsToIgnore id of the tasks to ignore when querying for {@link AssistContent}
+ * @param displayId id of the display to query tasks for Backlinks data
+ */
+ void triggerBacklinks(Set<Integer> taskIdsToIgnore, int displayId) {
+ mBgExecutor.execute(() -> {
+ ListenableFuture<InternalBacklinksData> backlinksData = getBacklinksData(
+ taskIdsToIgnore, displayId);
+ Futures.addCallback(backlinksData, new FutureCallback<>() {
+ @Override
+ public void onSuccess(@Nullable InternalBacklinksData result) {
+ if (result != null) {
+ mBacklinksLiveData.setValue(result);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ Log.e(TAG, "Error querying for Backlinks data", t);
+ }
+ }, mMainExecutor);
+
+ });
+ }
+
/** Returns a {@link LiveData} that holds the captured screenshot. */
LiveData<Bitmap> getScreenshot() {
return mScreenshotLiveData;
@@ -107,6 +181,11 @@
return mErrorLiveData;
}
+ /** Returns a {@link LiveData} that holds Backlinks data in {@link InternalBacklinksData}. */
+ LiveData<InternalBacklinksData> getBacklinksLiveData() {
+ return mBacklinksLiveData;
+ }
+
/**
* Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to
* {@link LiveData}.
@@ -148,21 +227,148 @@
return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
}
+ private ListenableFuture<InternalBacklinksData> getBacklinksData(Set<Integer> taskIdsToIgnore,
+ int displayId) {
+ return getAllRootTaskInfosOnDisplay(displayId)
+ .stream()
+ .filter(taskInfo -> shouldIncludeTask(taskInfo, taskIdsToIgnore))
+ .findFirst()
+ .map(this::getBacklinksDataForTaskId)
+ .orElse(Futures.immediateFuture(null));
+ }
+
+ private List<RootTaskInfo> getAllRootTaskInfosOnDisplay(int displayId) {
+ try {
+ return mAtmService.getAllRootTaskInfosOnDisplay(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, String.format("Error while querying for tasks on display %d", displayId), e);
+ return Collections.emptyList();
+ }
+ }
+
+ private boolean shouldIncludeTask(RootTaskInfo taskInfo, Set<Integer> taskIdsToIgnore) {
+ // Only consider tasks that shouldn't be ignored, are visible, running, and have a launcher
+ // icon. Furthermore, types such as launcher/home/dock/assistant are ignored.
+ return !taskIdsToIgnore.contains(taskInfo.taskId)
+ && taskInfo.isVisible
+ && taskInfo.isRunning
+ && taskInfo.numActivities > 0
+ && taskInfo.topActivity != null
+ && taskInfo.topActivityInfo != null
+ && taskInfo.childTaskIds.length > 0
+ && taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_STANDARD
+ && canAppStartThroughLauncher(taskInfo.topActivity.getPackageName());
+ }
+
+ private boolean canAppStartThroughLauncher(String packageName) {
+ return getMainLauncherIntentForPackage(packageName).resolveActivity(mPackageManager)
+ != null;
+ }
+
+ private ListenableFuture<InternalBacklinksData> getBacklinksDataForTaskId(
+ RootTaskInfo taskInfo) {
+ SettableFuture<InternalBacklinksData> backlinksData = SettableFuture.create();
+ int taskId = taskInfo.taskId;
+ mAssistContentRequester.requestAssistContent(taskId, assistContent ->
+ backlinksData.set(getBacklinksDataFromAssistContent(taskInfo, assistContent)));
+ return withTimeout(backlinksData, 5L, TimeUnit.SECONDS, newSingleThreadScheduledExecutor());
+ }
+
+ /**
+ * A utility method to get {@link InternalBacklinksData} to use for Backlinks functionality from
+ * {@link AssistContent} received from the app whose screenshot is taken.
+ *
+ * <p>There are multiple ways an app can provide deep-linkable data via {@link AssistContent}
+ * but Backlinks restricts to using only one way. The following is the ordered list based on
+ * preference:
+ * <ul>
+ * <li>{@link AssistContent#getWebUri()} is the most preferred way.
+ * <li>Second preference is given to {@link AssistContent#getIntent()} when the app provides
+ * the intent, see {@link AssistContent#isAppProvidedIntent()}.
+ * <li>The last preference is given to an {@link Intent} that is built using
+ * {@link Intent#ACTION_MAIN} and {@link Intent#CATEGORY_LAUNCHER}.
+ * </ul>
+ *
+ * @param taskInfo {@link RootTaskInfo} of the task which provided the {@link AssistContent}.
+ * @param content the {@link AssistContent} to map into Backlinks {@link ClipData}.
+ * @return {@link InternalBacklinksData} that represents the Backlinks data along with app icon.
+ */
+ private InternalBacklinksData getBacklinksDataFromAssistContent(RootTaskInfo taskInfo,
+ @Nullable AssistContent content) {
+ String appName = getAppNameOfTask(taskInfo);
+ String packageName = taskInfo.topActivity.getPackageName();
+ Drawable appIcon = taskInfo.topActivityInfo.loadIcon(mPackageManager);
+ ClipData mainLauncherIntent = ClipData.newIntent(appName,
+ getMainLauncherIntentForPackage(packageName));
+ InternalBacklinksData fallback = new InternalBacklinksData(mainLauncherIntent, appIcon);
+ if (content == null) {
+ return fallback;
+ }
+
+ // First preference is given to app provided uri.
+ if (content.isAppProvidedWebUri()) {
+ Uri uri = content.getWebUri();
+ Intent backlinksIntent = new Intent(ACTION_VIEW).setData(uri);
+ if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+ return new InternalBacklinksData(ClipData.newRawUri(appName, uri), appIcon);
+ }
+ }
+
+ // Second preference is given to app provided, hopefully deep-linking, intent.
+ if (content.isAppProvidedIntent()) {
+ Intent backlinksIntent = content.getIntent();
+ if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+ return new InternalBacklinksData(ClipData.newIntent(appName, backlinksIntent),
+ appIcon);
+ }
+ }
+
+ return fallback;
+ }
+
+ private boolean doesIntentResolveToSamePackage(Intent intentToResolve,
+ String requiredPackageName) {
+ ComponentName resolvedComponent = intentToResolve.resolveActivity(mPackageManager);
+ if (resolvedComponent == null) {
+ return false;
+ }
+
+ return resolvedComponent.getPackageName().equals(requiredPackageName);
+ }
+
+ private String getAppNameOfTask(RootTaskInfo taskInfo) {
+ return taskInfo.topActivityInfo.loadLabel(mPackageManager).toString();
+ }
+
+ private Intent getMainLauncherIntentForPackage(String packageName) {
+ return new Intent(ACTION_MAIN)
+ .addCategory(CATEGORY_LAUNCHER)
+ .setPackage(packageName);
+ }
+
/** Helper factory to help with injecting {@link AppClipsViewModel}. */
static final class Factory implements ViewModelProvider.Factory {
private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
private final ImageExporter mImageExporter;
+ private final IActivityTaskManager mAtmService;
+ private final AssistContentRequester mAssistContentRequester;
+ private final PackageManager mPackageManager;
@Main
private final Executor mMainExecutor;
@Background
private final Executor mBgExecutor;
@Inject
- Factory(AppClipsCrossProcessHelper appClipsCrossProcessHelper, ImageExporter imageExporter,
- @Main Executor mainExecutor, @Background Executor bgExecutor) {
+ Factory(AppClipsCrossProcessHelper appClipsCrossProcessHelper, ImageExporter imageExporter,
+ IActivityTaskManager atmService, AssistContentRequester assistContentRequester,
+ PackageManager packageManager, @Main Executor mainExecutor,
+ @Background Executor bgExecutor) {
mAppClipsCrossProcessHelper = appClipsCrossProcessHelper;
mImageExporter = imageExporter;
+ mAtmService = atmService;
+ mAssistContentRequester = assistContentRequester;
+ mPackageManager = packageManager;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
}
@@ -176,7 +382,8 @@
//noinspection unchecked
return (T) new AppClipsViewModel(mAppClipsCrossProcessHelper, mImageExporter,
- mMainExecutor, mBgExecutor);
+ mAtmService, mAssistContentRequester, mPackageManager, mMainExecutor,
+ mBgExecutor);
}
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
similarity index 66%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
index d8af3fa..0e312f9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.screenshot.appclips
-import com.android.systemui.kosmos.Kosmos
+import android.content.ClipData
+import android.graphics.drawable.Drawable
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+/** A class to hold the [ClipData] for backlinks and the corresponding app's [Drawable] icon. */
+internal data class InternalBacklinksData(val clipData: ClipData, val appIcon: Drawable)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 442b387..0fefa0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -128,8 +128,9 @@
}
}
launch {
- viewModel.previewAction.collect { onClick ->
- previewView.setOnClickListener { onClick?.invoke() }
+ viewModel.previewAction.collect { action ->
+ previewView.setOnClickListener { action?.onClick?.invoke() }
+ previewView.contentDescription = action?.contentDescription
}
}
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index 3f99bc4..25420d4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -31,8 +31,8 @@
val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim
private val _badge = MutableStateFlow<Drawable?>(null)
val badge: StateFlow<Drawable?> = _badge
- private val _previewAction = MutableStateFlow<(() -> Unit)?>(null)
- val previewAction: StateFlow<(() -> Unit)?> = _previewAction
+ private val _previewAction = MutableStateFlow<PreviewAction?>(null)
+ val previewAction: StateFlow<PreviewAction?> = _previewAction
private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>())
val actions: StateFlow<List<ActionButtonViewModel>> = _actions
private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED)
@@ -57,8 +57,8 @@
_badge.value = badge
}
- fun setPreviewAction(onClick: () -> Unit) {
- _previewAction.value = onClick
+ fun setPreviewAction(previewAction: PreviewAction) {
+ _previewAction.value = previewAction
}
fun addAction(
@@ -149,6 +149,11 @@
}
}
+data class PreviewAction(
+ val contentDescription: CharSequence,
+ val onClick: () -> Unit,
+)
+
enum class AnimationState {
NOT_STARTED,
ENTRANCE_STARTED, // The first 200ms of the entrance animation
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index d382b7a..a62edcb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -25,6 +25,7 @@
import android.app.Activity;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Gravity;
@@ -32,7 +33,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
@@ -152,18 +155,27 @@
Configuration configuration = getResources().getConfiguration();
int orientation = configuration.orientation;
- int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
+ int windowWidth = getWindowAvailableWidth();
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
boolean shouldBeFullWidth = getIntent()
.getBooleanExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, false);
- lp.width = (shouldBeFullWidth ? screenWidth : screenWidth / 2) - horizontalMargin * 2;
+ lp.width = (shouldBeFullWidth ? windowWidth : windowWidth / 2) - horizontalMargin * 2;
} else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- lp.width = screenWidth - horizontalMargin * 2;
+ lp.width = windowWidth - horizontalMargin * 2;
}
frame.setLayoutParams(lp);
+ }
+ private int getWindowAvailableWidth() {
+ final WindowMetrics metrics = getWindowManager().getCurrentWindowMetrics();
+ // Gets all excluding insets
+ final WindowInsets windowInsets = metrics.getWindowInsets();
+ Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.displayCutout());
+ int insetsWidth = insets.right + insets.left;
+ return metrics.getBounds().width() - insetsWidth;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflater.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflater.kt
index 468a873..cebc59b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/binder/BrightnessMirrorInflater.kt
@@ -21,6 +21,7 @@
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
+import androidx.core.view.setPadding
import com.android.systemui.res.R
import com.android.systemui.settings.brightness.BrightnessSliderController
@@ -33,7 +34,15 @@
val frame =
(LayoutInflater.from(context).inflate(R.layout.brightness_mirror_container, null)
as ViewGroup)
- .apply { isVisible = true }
+ .apply {
+ isVisible = true
+ // Match BrightnessMirrorController padding
+ setPadding(
+ context.resources.getDimensionPixelSize(
+ R.dimen.rounded_slider_background_padding
+ )
+ )
+ }
val sliderController = sliderControllerFactory.create(context, frame)
sliderController.init()
frame.addView(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt
index 2651a994..79e8b87 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.settings.brightness.ui.viewModel
import android.content.res.Resources
+import android.util.Log
import android.view.View
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
@@ -67,25 +68,45 @@
override fun setLocationAndSize(view: View) {
view.getLocationInWindow(tempPosition)
val padding = resources.getDimensionPixelSize(R.dimen.rounded_slider_background_padding)
- _toggleSlider?.rootView?.setPadding(padding, padding, padding, padding)
// Account for desired padding
_locationAndSize.value =
LocationAndSize(
- yOffset = tempPosition[1] - padding,
+ yOffsetFromContainer = view.findTopFromContainer() - padding,
+ yOffsetFromWindow = tempPosition[1] - padding,
width = view.measuredWidth + 2 * padding,
height = view.measuredHeight + 2 * padding,
)
}
+ private fun View.findTopFromContainer(): Int {
+ var out = 0
+ var view = this
+ while (view.id != R.id.quick_settings_container) {
+ out += view.top
+ val parent = view.parent as? View
+ if (parent == null) {
+ Log.wtf(TAG, "Couldn't find container in parents of $this")
+ break
+ }
+ view = parent
+ }
+ return out
+ }
+
// Callbacks are used for indicating reinflation when the config changes in some ways (like
// density). However, we don't need that as we recompose the view anyway
override fun addCallback(listener: MirrorController.BrightnessMirrorListener) {}
override fun removeCallback(listener: MirrorController.BrightnessMirrorListener) {}
+
+ companion object {
+ private const val TAG = "BrightnessMirrorViewModel"
+ }
}
data class LocationAndSize(
- val yOffset: Int = 0,
+ val yOffsetFromContainer: Int = 0,
+ val yOffsetFromWindow: Int = 0,
val width: Int = 0,
val height: Int = 0,
)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 2def6c7..59c1592 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -37,7 +37,6 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.Flags.glanceableHubFullscreenSwipe
import com.android.systemui.ambient.touch.TouchMonitor
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.communal.dagger.Communal
@@ -309,13 +308,9 @@
)
collectFlow(containerView, keyguardInteractor.isDreaming, { isDreaming = it })
- if (glanceableHubFullscreenSwipe()) {
- communalContainerWrapper = CommunalWrapper(containerView.context)
- communalContainerWrapper?.addView(communalContainerView)
- return communalContainerWrapper!!
- } else {
- return containerView
- }
+ communalContainerWrapper = CommunalWrapper(containerView.context)
+ communalContainerWrapper?.addView(communalContainerView)
+ return communalContainerWrapper!!
}
/**
@@ -371,8 +366,7 @@
// and the touch is within the horizontal notification band on the screen, do not process
// the touch.
if (
- glanceableHubFullscreenSwipe() &&
- !hubShowing &&
+ !hubShowing &&
!notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y)
) {
return false
@@ -389,17 +383,7 @@
val hubOccluded = anyBouncerShowing || shadeShowing
if (isDown && !hubOccluded) {
- if (glanceableHubFullscreenSwipe()) {
- isTrackingHubTouch = true
- } else {
- val x = ev.rawX
- val inOpeningSwipeRegion: Boolean = x >= view.width - rightEdgeSwipeRegionWidth
- if (inOpeningSwipeRegion || hubShowing) {
- // Steal touch events when the hub is open, or if the touch started in the
- // opening gesture region.
- isTrackingHubTouch = true
- }
- }
+ isTrackingHubTouch = true
}
if (isTrackingHubTouch) {
@@ -419,20 +403,12 @@
private fun dispatchTouchEvent(view: View, ev: MotionEvent): Boolean {
try {
var handled = false
- if (glanceableHubFullscreenSwipe()) {
- communalContainerWrapper?.dispatchTouchEvent(ev) {
- if (it) {
- handled = true
- }
+ communalContainerWrapper?.dispatchTouchEvent(ev) {
+ if (it) {
+ handled = true
}
- return handled || hubShowing
- } else {
- view.dispatchTouchEvent(ev)
- // Return true regardless of dispatch result as some touches at the start of a
- // gesture
- // may return false from dispatchTouchEvent.
- return true
}
+ return handled || hubShowing
} finally {
powerManager.userActivity(
SystemClock.uptimeMillis(),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 9624e0f..16d10ab 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -148,7 +148,7 @@
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
@@ -775,7 +775,7 @@
@Main CoroutineDispatcher mainDispatcher,
KeyguardTransitionInteractor keyguardTransitionInteractor,
DumpManager dumpManager,
- KeyguardLongPressViewModel keyguardLongPressViewModel,
+ KeyguardTouchHandlingViewModel keyguardTouchHandlingViewModel,
KeyguardInteractor keyguardInteractor,
ActivityStarter activityStarter,
SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
@@ -970,7 +970,7 @@
mKeyguardClockInteractor = keyguardClockInteractor;
KeyguardLongPressViewBinder.bind(
mView.requireViewById(R.id.keyguard_long_press),
- keyguardLongPressViewModel,
+ keyguardTouchHandlingViewModel,
() -> {
onEmptySpaceClick();
return Unit.INSTANCE;
@@ -1044,6 +1044,10 @@
mView.setTranslationY(0f);
})
.start();
+ } else {
+ mView.postDelayed(() -> {
+ instantCollapse();
+ }, unlockAnimationStartDelay);
}
}
}
@@ -3047,7 +3051,7 @@
if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
mUpdateMonitor.requestActiveUnlock(
- ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY,
"lockScreenEmptySpaceTap");
} else {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
@@ -3390,7 +3394,9 @@
/** Updates the views to the initial state for the fold to AOD animation. */
@Override
public void prepareFoldToAodAnimation() {
- if (!MigrateClocksToBlueprint.isEnabled()) {
+ if (MigrateClocksToBlueprint.isEnabled()) {
+ setDozing(true /* dozing */, false /* animate */);
+ } else {
// Force show AOD UI even if we are not locked
showAodUi();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e41f99b..1c223db 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -369,7 +369,9 @@
}
mFalsingCollector.onTouchEvent(ev);
- mPulsingWakeupGestureHandler.onTouchEvent(ev);
+ if (!SceneContainerFlag.isEnabled()) {
+ mPulsingWakeupGestureHandler.onTouchEvent(ev);
+ }
if (!SceneContainerFlag.isEnabled()
&& mGlanceableHubContainerController.onTouchEvent(ev)) {
@@ -427,7 +429,11 @@
} else {
return logDownDispatch(ev, "hidden or hiding", true);
}
+ } else {
+ mShadeLogger.d("NSWVC: bouncer not showing");
}
+ } else {
+ mShadeLogger.d("NSWVC: touch not within view");
}
} else if (mIsTrackingBarGesture) {
final boolean sendToStatusBar = mStatusBarViewController.sendTouchToView(ev);
@@ -437,6 +443,9 @@
return logDownDispatch(ev, "sending bar gesture to status bar",
sendToStatusBar);
}
+ if (isDown) {
+ mShadeLogger.logNoTouchDispatch(mIsTrackingBarGesture, mExpandAnimationRunning);
+ }
return logDownDispatch(ev, "no custom touch dispatch of down event", null);
}
@@ -698,12 +707,22 @@
@Override
public void dump(PrintWriter pw, String[] args) {
+ pw.print(" mExpandingBelowNotch=");
+ pw.println(mExpandingBelowNotch);
pw.print(" mExpandAnimationRunning=");
pw.println(mExpandAnimationRunning);
- pw.print(" mTouchCancelled=");
- pw.println(mTouchCancelled);
+ pw.print(" mExternalTouchIntercepted=");
+ pw.println(mExternalTouchIntercepted);
+ pw.print(" mIsOcclusionTransitionRunning=");
+ pw.println(mIsOcclusionTransitionRunning);
+ pw.print(" mIsTrackingBarGesture=");
+ pw.println(mIsTrackingBarGesture);
+ pw.print(" mLaunchAnimationTimeout=");
+ pw.println(mLaunchAnimationTimeout);
pw.print(" mTouchActive=");
pw.println(mTouchActive);
+ pw.print(" mTouchCancelled=");
+ pw.println(mTouchCancelled);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index fe4832f0..062327d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -47,17 +47,19 @@
* display state, wake-ups are handled by [com.android.systemui.doze.DozeSensors].
*/
@SysUISingleton
-class PulsingGestureListener @Inject constructor(
- private val falsingManager: FalsingManager,
- private val dockManager: DockManager,
- private val powerInteractor: PowerInteractor,
- private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
- private val statusBarStateController: StatusBarStateController,
- private val shadeLogger: ShadeLogger,
- private val dozeInteractor: DozeInteractor,
- userTracker: UserTracker,
- tunerService: TunerService,
- dumpManager: DumpManager
+class PulsingGestureListener
+@Inject
+constructor(
+ private val falsingManager: FalsingManager,
+ private val dockManager: DockManager,
+ private val powerInteractor: PowerInteractor,
+ private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+ private val statusBarStateController: StatusBarStateController,
+ private val shadeLogger: ShadeLogger,
+ private val dozeInteractor: DozeInteractor,
+ userTracker: UserTracker,
+ tunerService: TunerService,
+ dumpManager: DumpManager
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
private var doubleTapEnabled = false
private var singleTapEnabled = false
@@ -66,21 +68,27 @@
val tunable = Tunable { key: String?, _: String? ->
when (key) {
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE ->
- doubleTapEnabled = ambientDisplayConfiguration.doubleTapGestureEnabled(
- userTracker.userId)
+ doubleTapEnabled =
+ ambientDisplayConfiguration.doubleTapGestureEnabled(userTracker.userId)
Settings.Secure.DOZE_TAP_SCREEN_GESTURE ->
- singleTapEnabled = ambientDisplayConfiguration.tapGestureEnabled(
- userTracker.userId)
+ singleTapEnabled =
+ ambientDisplayConfiguration.tapGestureEnabled(userTracker.userId)
}
}
- tunerService.addTunable(tunable,
- Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
- Settings.Secure.DOZE_TAP_SCREEN_GESTURE)
+ tunerService.addTunable(
+ tunable,
+ Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
+ Settings.Secure.DOZE_TAP_SCREEN_GESTURE
+ )
dumpManager.registerDumpable(this)
}
override fun onSingleTapUp(e: MotionEvent): Boolean {
+ return onSingleTapUp(e.x, e.y)
+ }
+
+ fun onSingleTapUp(x: Float, y: Float): Boolean {
val isNotDocked = !dockManager.isDocked
shadeLogger.logSingleTapUp(statusBarStateController.isDozing, singleTapEnabled, isNotDocked)
if (statusBarStateController.isDozing && singleTapEnabled && isNotDocked) {
@@ -89,11 +97,13 @@
shadeLogger.logSingleTapUpFalsingState(proximityIsNotNear, isNotAFalseTap)
if (proximityIsNotNear && isNotAFalseTap) {
shadeLogger.d("Single tap handled, requesting centralSurfaces.wakeUpIfDozing")
- dozeInteractor.setLastTapToWakePosition(Point(e.x.toInt(), e.y.toInt()))
+ dozeInteractor.setLastTapToWakePosition(Point(x.toInt(), y.toInt()))
powerInteractor.wakeUpIfDozing("PULSING_SINGLE_TAP", PowerManager.WAKE_REASON_TAP)
}
+
return true
}
+
shadeLogger.d("onSingleTapUp event ignored")
return false
}
@@ -103,10 +113,18 @@
* motion events for a double tap.
*/
override fun onDoubleTapEvent(e: MotionEvent): Boolean {
+ if (e.actionMasked != MotionEvent.ACTION_UP) {
+ return false
+ }
+
+ return onDoubleTapEvent()
+ }
+
+ fun onDoubleTapEvent(): Boolean {
// React to the [MotionEvent.ACTION_UP] event after double tap is detected. Falsing
// checks MUST be on the ACTION_UP event.
- if (e.actionMasked == MotionEvent.ACTION_UP &&
- statusBarStateController.isDozing &&
+ if (
+ statusBarStateController.isDozing &&
(doubleTapEnabled || singleTapEnabled) &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseDoubleTap
@@ -114,6 +132,7 @@
powerInteractor.wakeUpIfDozing("PULSING_DOUBLE_TAP", PowerManager.WAKE_REASON_TAP)
return true
}
+
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 004db16..ce321dc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade
import android.view.MotionEvent
+import androidx.compose.ui.Alignment
import com.android.systemui.assist.AssistManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -24,6 +25,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -175,11 +177,16 @@
sceneInteractor.changeScene(
SceneFamilies.NotifShade,
"ShadeController.animateExpandShade",
+ OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
)
}
override fun expandToQs() {
- sceneInteractor.changeScene(SceneFamilies.QuickSettings, "ShadeController.animateExpandQs")
+ sceneInteractor.changeScene(
+ SceneFamilies.QuickSettings,
+ "ShadeController.animateExpandQs",
+ OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
+ )
}
override fun setVisibilityListener(listener: ShadeVisibilityListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index b5b46f1..66a310c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -72,79 +72,78 @@
},
{
"QsTrackingNotStarted: downTime=$str1,initTouchY=$int1,y=$int2,h=$long1," +
- "slop=$double1,qsExpanded=$bool1,keyguardShowing=$bool2,qsExpansion=$bool3"
+ "slop=$double1,qsExpanded=$bool1,keyguardShowing=$bool2,qsExpansion=$bool3"
}
)
}
fun logMotionEvent(event: MotionEvent, message: String) {
buffer.log(
- TAG,
- LogLevel.VERBOSE,
- {
- str1 = message
- long1 = event.eventTime
- long2 = event.downTime
- int1 = event.action
- int2 = event.classification
- },
- {
- "$str1: eventTime=$long1,downTime=$long2,action=$int1,class=$int2"
- }
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = message
+ long1 = event.eventTime
+ long2 = event.downTime
+ int1 = event.action
+ int2 = event.classification
+ },
+ { "$str1: eventTime=$long1,downTime=$long2,action=$int1,class=$int2" }
)
}
/** Logs motion event dispatch results from NotificationShadeWindowViewController. */
fun logShadeWindowDispatch(event: MotionEvent, message: String, result: Boolean?) {
buffer.log(
- TAG,
- LogLevel.VERBOSE,
- {
- str1 = message
- long1 = event.eventTime
- long2 = event.downTime
- },
- {
- val prefix = when (result) {
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = message
+ long1 = event.eventTime
+ long2 = event.downTime
+ },
+ {
+ val prefix =
+ when (result) {
true -> "SHADE TOUCH REROUTED"
false -> "SHADE TOUCH BLOCKED"
null -> "SHADE TOUCH DISPATCHED"
}
- "$prefix: eventTime=$long1,downTime=$long2, reason=$str1"
- }
+ "$prefix: eventTime=$long1,downTime=$long2, reason=$str1"
+ }
)
}
fun logMotionEventStatusBarState(event: MotionEvent, statusBarState: Int, message: String) {
buffer.log(
- TAG,
- LogLevel.VERBOSE,
- {
- str1 = message
- long1 = event.eventTime
- long2 = event.downTime
- int1 = event.action
- int2 = statusBarState
- double1 = event.y.toDouble()
- },
- {
- "$str1\neventTime=$long1,downTime=$long2,y=$double1,action=$int1," +
- "statusBarState=${when (int2) {
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = message
+ long1 = event.eventTime
+ long2 = event.downTime
+ int1 = event.action
+ int2 = statusBarState
+ double1 = event.y.toDouble()
+ },
+ {
+ "$str1\neventTime=$long1,downTime=$long2,y=$double1,action=$int1," +
+ "statusBarState=${when (int2) {
0 -> "SHADE"
1 -> "KEYGUARD"
2 -> "SHADE_LOCKED"
else -> "UNKNOWN:$int2"
}}"
- }
+ }
)
}
fun logExpansionChanged(
- message: String,
- fraction: Float,
- expanded: Boolean,
- tracking: Boolean,
- dragDownPxAmount: Float,
+ message: String,
+ fraction: Float,
+ expanded: Boolean,
+ tracking: Boolean,
+ dragDownPxAmount: Float,
) {
buffer.log(
TAG,
@@ -158,7 +157,8 @@
},
{
"$str1 fraction=$double1,expanded=$bool1," +
- "tracking=$bool2," + "dragDownPxAmount=$dragDownPxAmount"
+ "tracking=$bool2," +
+ "dragDownPxAmount=$dragDownPxAmount"
}
)
}
@@ -176,24 +176,17 @@
}
fun logQsExpandImmediateChanged(newValue: Boolean) {
- buffer.log(
- TAG,
- LogLevel.VERBOSE,
- {
- bool1 = newValue
- },
- { "qsExpandImmediate=$bool1" }
- )
+ buffer.log(TAG, LogLevel.VERBOSE, { bool1 = newValue }, { "qsExpandImmediate=$bool1" })
}
fun logQsExpansionChanged(
- message: String,
- qsExpanded: Boolean,
- qsMinExpansionHeight: Int,
- qsMaxExpansionHeight: Int,
- stackScrollerOverscrolling: Boolean,
- qsAnimatorExpand: Boolean,
- animatingQs: Boolean
+ message: String,
+ qsExpanded: Boolean,
+ qsMinExpansionHeight: Int,
+ qsMaxExpansionHeight: Int,
+ stackScrollerOverscrolling: Boolean,
+ qsAnimatorExpand: Boolean,
+ animatingQs: Boolean
) {
buffer.log(
TAG,
@@ -227,8 +220,10 @@
},
{
"PulsingGestureListener#onSingleTapUp all of this must true for single " +
- "tap to be detected: isDozing: $bool1, singleTapEnabled: $bool2, isNotDocked: $bool3"
- })
+ "tap to be detected: isDozing: $bool1, singleTapEnabled: $bool2," +
+ " isNotDocked: $bool3"
+ }
+ )
}
fun logSingleTapUpFalsingState(proximityIsNotNear: Boolean, isNotFalseTap: Boolean) {
@@ -247,9 +242,9 @@
}
fun logNotInterceptingTouchInstantExpanding(
- instantExpanding: Boolean,
- notificationsDragEnabled: Boolean,
- touchDisabled: Boolean
+ instantExpanding: Boolean,
+ notificationsDragEnabled: Boolean,
+ touchDisabled: Boolean
) {
buffer.log(
TAG,
@@ -276,12 +271,12 @@
}
fun logFlingExpands(
- vel: Float,
- vectorVel: Float,
- interactionType: Int,
- minVelocityPxPerSecond: Float,
- expansionOverHalf: Boolean,
- allowExpandForSmallExpansion: Boolean
+ vel: Float,
+ vectorVel: Float,
+ interactionType: Int,
+ minVelocityPxPerSecond: Float,
+ expansionOverHalf: Boolean,
+ allowExpandForSmallExpansion: Boolean
) {
buffer.log(
TAG,
@@ -294,9 +289,11 @@
bool1 = expansionOverHalf
bool2 = allowExpandForSmallExpansion
},
- { "NPVC flingExpands called with vel: $long1, vectorVel: $long2, " +
+ {
+ "NPVC flingExpands called with vel: $long1, vectorVel: $long2, " +
"interactionType: $int1, minVelocityPxPerSecond: $double1 " +
- "expansionOverHalf: $bool1, allowExpandForSmallExpansion: $bool2" }
+ "expansionOverHalf: $bool1, allowExpandForSmallExpansion: $bool2"
+ }
)
}
@@ -338,9 +335,7 @@
buffer.log(
TAG,
LogLevel.VERBOSE,
- {
- str1 = panelState.panelStateToString()
- },
+ { str1 = panelState.panelStateToString() },
{ "New panel State: $str1" }
)
}
@@ -357,12 +352,13 @@
)
}
- private fun flingTypeToString(flingType: Int) = when (flingType) {
- FLING_EXPAND -> "FLING_EXPAND"
- FLING_COLLAPSE -> "FLING_COLLAPSE"
- FLING_HIDE -> "FLING_HIDE"
- else -> "UNKNOWN"
- }
+ private fun flingTypeToString(flingType: Int) =
+ when (flingType) {
+ FLING_EXPAND -> "FLING_EXPAND"
+ FLING_COLLAPSE -> "FLING_COLLAPSE"
+ FLING_HIDE -> "FLING_HIDE"
+ else -> "UNKNOWN"
+ }
fun logSplitShadeChanged(splitShadeEnabled: Boolean) {
buffer.log(
@@ -392,8 +388,26 @@
},
{
"CentralSurfaces updateNotificationPanelTouchState set disabled to: $bool1\n" +
- "isGoingToSleep: $bool2, !shouldControlScreenOff: $bool3," +
- "!mDeviceInteractive: $bool4, !isPulsing: $str1"
+ "isGoingToSleep: $bool2, !shouldControlScreenOff: $bool3," +
+ "!mDeviceInteractive: $bool4, !isPulsing: $str1"
+ }
+ )
+ }
+
+ fun logNoTouchDispatch(
+ isTrackingBarGesture: Boolean,
+ isExpandAnimationRunning: Boolean,
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ bool1 = isTrackingBarGesture
+ bool2 = isExpandAnimationRunning
+ },
+ {
+ "NSWVC: touch not dispatched: isTrackingBarGesture: $bool1, " +
+ "isExpandAnimationRunning: $bool2"
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 7e1a310..4d43ad5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -15,7 +15,10 @@
*/
package com.android.systemui.shade.data.repository
+import android.content.Context
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
@@ -104,6 +107,9 @@
val shadeMode: StateFlow<ShadeMode>
+ /** Whether dual shade should be aligned to the bottom (true) or to the top (false). */
+ val isDualShadeAlignedToBottom: Boolean
+
/** True when QS is taking up the entire screen, i.e. fully expanded on a non-unfolded phone. */
@Deprecated("Use ShadeInteractor instead") val legacyQsFullscreen: StateFlow<Boolean>
@@ -174,7 +180,8 @@
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
+class ShadeRepositoryImpl @Inject constructor(@Application applicationContext: Context) :
+ ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
@@ -223,6 +230,9 @@
val _shadeMode = MutableStateFlow(if (DualShade.isEnabled) ShadeMode.Dual else ShadeMode.Single)
override val shadeMode: StateFlow<ShadeMode> = _shadeMode.asStateFlow()
+ override val isDualShadeAlignedToBottom =
+ applicationContext.resources.getBoolean(R.bool.config_dualShadeAlignedToBottom)
+
override fun setShadeMode(shadeMode: ShadeMode) {
_shadeMode.value = shadeMode
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 18407cc..ef0a842 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade.domain.interactor
+import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.shared.model.ShadeMode
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -53,6 +54,9 @@
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean>
+
+ /** How to align the shade content. */
+ val shadeAlignment: ShadeAlignment
}
/** ShadeInteractor methods with implementations that differ between non-empty impls. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
index bb4baa3..6226d07 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -46,4 +47,5 @@
override val isShadeTouchable: Flow<Boolean> = inactiveFlowBoolean
override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean
override val shadeMode: StateFlow<ShadeMode> = MutableStateFlow(ShadeMode.Single)
+ override val shadeAlignment: ShadeAlignment = ShadeAlignment.Top
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index 4014512..55f019b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -24,6 +24,8 @@
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.shared.model.ShadeAlignment
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
@@ -51,6 +53,7 @@
keyguardRepository: KeyguardRepository,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
powerInteractor: PowerInteractor,
+ shadeRepository: ShadeRepository,
userSetupRepository: UserSetupRepository,
userSwitcherInteractor: UserSwitcherInteractor,
private val baseShadeInteractor: BaseShadeInteractor,
@@ -99,6 +102,13 @@
}
}
+ override val shadeAlignment: ShadeAlignment =
+ if (shadeRepository.isDualShadeAlignedToBottom) {
+ ShadeAlignment.Bottom
+ } else {
+ ShadeAlignment.Top
+ }
+
override val isExpandToQsEnabled: Flow<Boolean> =
combine(
disableFlagsRepository.disableFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index d5953ca..e525b86 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -19,6 +19,7 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
@@ -96,7 +97,7 @@
}
override fun showAodUi() {
- sceneInteractor.changeScene(Scenes.Lockscreen, "showAodUi")
+ sceneInteractor.changeScene(Scenes.Lockscreen, "showAodUi", sceneState = KeyguardState.AOD)
// TODO(b/330311871) implement transition to AOD
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
similarity index 66%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
index d8af3fa..06905379 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
@@ -14,8 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.shade.shared.model
-import com.android.systemui.kosmos.Kosmos
+/** Enumerates all supported alignments of the shade. */
+sealed interface ShadeAlignment {
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+ /** Aligns the shade to the top. */
+ data object Top : ShadeAlignment
+
+ /** Aligns the shade to the bottom. */
+ data object Bottom : ShadeAlignment
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
index 0314091..6551854 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
@@ -22,6 +22,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -38,6 +39,7 @@
constructor(
@Application applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
+ shadeInteractor: ShadeInteractor
) {
/** The scene to show in the background when the overlay shade is open. */
val backgroundScene: StateFlow<SceneKey> =
@@ -49,6 +51,9 @@
initialValue = Scenes.Lockscreen,
)
+ /** Dictates the alignment of the overlay shade panel on the screen. */
+ val panelAlignment = shadeInteractor.shadeAlignment
+
/** Notifies that the user has clicked the semi-transparent background scrim. */
fun onScrimClicked() {
sceneInteractor.changeScene(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index b0100b9..2b2aac64 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -37,7 +37,6 @@
import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import java.util.concurrent.atomic.AtomicBoolean
@@ -60,7 +59,6 @@
@Application private val applicationScope: CoroutineScope,
val qsSceneAdapter: QSSceneAdapter,
val shadeHeaderViewModel: ShadeHeaderViewModel,
- val notifications: NotificationsPlaceholderViewModel,
val brightnessMirrorViewModel: BrightnessMirrorViewModel,
val mediaCarouselInteractor: MediaCarouselInteractor,
shadeInteractor: ShadeInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 95cabfb..2f3fc729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -386,6 +386,11 @@
mStatusBarStateListener.onDozingChanged(mStatusBarStateController.isDozing());
}
+ @Nullable
+ public ViewGroup getIndicationArea() {
+ return mIndicationArea;
+ }
+
public void setIndicationArea(ViewGroup indicationArea) {
mIndicationArea = indicationArea;
mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
@@ -1665,11 +1670,6 @@
private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
- public void onStateChanged(int newState) {
- setVisible(newState == StatusBarState.KEYGUARD);
- }
-
- @Override
public void onDozingChanged(boolean dozing) {
if (mDozing == dozing) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 04a413a..240953d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -291,8 +291,9 @@
}
private void updateMediaMetaData(MediaListener callback) {
- callback.onPrimaryMetadataOrStateChanged(mMediaMetadata,
- getMediaControllerPlaybackState(mMediaController));
+ int playbackState = getMediaControllerPlaybackState(mMediaController);
+ mHandler.post(
+ () -> callback.onPrimaryMetadataOrStateChanged(mMediaMetadata, playbackState));
}
public void removeCallback(MediaListener callback) {
@@ -437,9 +438,11 @@
private void updateMediaMetaData(List<MediaListener> callbacks) {
@PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController);
- for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).onPrimaryMetadataOrStateChanged(mMediaMetadata, state);
- }
+ mHandler.post(() -> {
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).onPrimaryMetadataOrStateChanged(mMediaMetadata, state);
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 307e702..9b1e782 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
@@ -399,7 +400,7 @@
public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) {
return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
- null /* userMessageContent */, null /* authBypassCheck */);
+ null /* userMessageContent */, null /* authBypassCheck */);
}
/**
@@ -420,6 +421,12 @@
PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
@Nullable String userMessageContent,
@Nullable AuthBypassPredicate authBypassCheck) {
+ if (ExpandHeadsUpOnInlineReply.isEnabled()) {
+ return activateRemoteInputOnExpanded(view, inputs, input, pendingIntent,
+ editedSuggestionInfo, userMessageContent,
+ authBypassCheck);
+ }
+
ViewParent p = view.getParent();
RemoteInputView riv = null;
ExpandableNotificationRow row = null;
@@ -491,6 +498,86 @@
return true;
}
+ /**
+ * Activates a given {@link RemoteInput} on the expanded notification.
+ * If the given notification is not expanded, this method will expand the notification
+ * first and after that activate remote input on the expanded.
+ * @param view The view of the action button or suggestion chip that was tapped.
+ * @param inputs The remote inputs that need to be sent to the app.
+ * @param input The remote input that needs to be activated.
+ * @param pendingIntent The pending intent to be sent to the app.
+ * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or
+ * {@code null} if the user is not editing a smart reply.
+ * @param userMessageContent User-entered text with which to initialize the remote input view.
+ * @param authBypassCheck Optional auth bypass check associated with this remote input
+ * activation. If {@code null}, we never bypass.
+ * @return Whether the {@link RemoteInput} was activated.
+ */
+ public boolean activateRemoteInputOnExpanded(View view, RemoteInput[] inputs, RemoteInput input,
+ PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
+ @Nullable String userMessageContent,
+ @Nullable AuthBypassPredicate authBypassCheck) {
+ ViewParent p = view.getParent();
+ RemoteInputView riv = null;
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ if (pv.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
+ row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view);
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+
+ if (row == null) {
+ return false;
+ }
+
+ final boolean deferBouncer = authBypassCheck != null;
+ if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) {
+ return true;
+ }
+
+ if (!row.getPrivateLayout().getExpandedChild().isShown()) {
+ // The expanded layout is selected, but it's not shown yet, let's wait on it to
+ // show before we do the animation.
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> {
+ activateRemoteInputOnExpanded(view, inputs, input, pendingIntent,
+ editedSuggestionInfo, userMessageContent, authBypassCheck);
+ });
+ return true;
+ }
+
+ riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
+ if (riv == null) {
+ return false;
+ }
+
+ if (!riv.isAttachedToWindow()) {
+ // if we still didn't find a view that is attached, let's abort.
+ return false;
+ }
+
+ riv.getController().setPendingIntent(pendingIntent);
+ riv.getController().setRemoteInput(input);
+ riv.getController().setRemoteInputs(inputs);
+ riv.getController().setEditedSuggestionInfo(editedSuggestionInfo);
+ riv.focusAnimated();
+ if (userMessageContent != null) {
+ riv.setEditTextContent(userMessageContent);
+ }
+ if (deferBouncer) {
+ final ExpandableNotificationRow finalRow = row;
+ riv.getController().setBouncerChecker(() ->
+ !authBypassCheck.canSendRemoteInputWithoutBouncer()
+ && showBouncerForRemoteInput(view, pendingIntent, finalRow));
+ }
+
+ return true;
+ }
+
private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 9a82ecf..28e3a83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -40,6 +40,7 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -261,14 +262,17 @@
viewState.hasItemsInStableShelf = false;
}
- final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
+ final float stackBottom = SceneContainerFlag.isEnabled()
+ ? ambientState.getStackTop() + ambientState.getStackHeight()
+ : ambientState.getStackY() + ambientState.getStackHeight();
+
if (viewState.hidden) {
// if the shelf is hidden, position it at the end of the stack (plus the clip
// padding), such that when it appears animated, it will smoothly move in from the
// bottom, without jump cutting any notifications
- viewState.setYTranslation(stackEnd + mPaddingBetweenElements);
+ viewState.setYTranslation(stackBottom + mPaddingBetweenElements);
} else {
- viewState.setYTranslation(stackEnd - viewState.height);
+ viewState.setYTranslation(stackBottom - viewState.height);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index d0702fc..bbf0ae1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -69,7 +69,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -148,11 +147,6 @@
@VisibleForTesting float mScaleToFitNewIconSize = 1;
private StatusBarIcon mIcon;
@ViewDebug.ExportedProperty private String mSlot;
- private Drawable mNumberBackground;
- private Paint mNumberPain;
- private int mNumberX;
- private int mNumberY;
- private String mNumberText;
private StatusBarNotification mNotification;
private final boolean mBlocked;
private Configuration mConfiguration;
@@ -201,10 +195,6 @@
mDozer = new NotificationDozeHelper();
mBlocked = blocked;
mSlot = slot;
- mNumberPain = new Paint();
- mNumberPain.setTextAlign(Paint.Align.CENTER);
- mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color));
- mNumberPain.setAntiAlias(true);
setNotification(sbn);
setScaleType(ScaleType.CENTER);
mConfiguration = new Configuration(context.getResources().getConfiguration());
@@ -410,8 +400,6 @@
&& mIcon.iconLevel == icon.iconLevel;
final boolean visibilityEquals = mIcon != null
&& mIcon.visible == icon.visible;
- final boolean numberEquals = mIcon != null
- && mIcon.number == icon.number;
mIcon = icon.clone();
setContentDescription(icon.contentDescription);
if (!iconEquals) {
@@ -425,20 +413,6 @@
setImageLevel(icon.iconLevel);
}
- if (!numberEquals) {
- if (icon.number > 0 && getContext().getResources().getBoolean(
- R.bool.config_statusBarShowNumber)) {
- if (mNumberBackground == null) {
- mNumberBackground = getContext().getResources().getDrawable(
- R.drawable.ic_notification_overlay);
- }
- placeNumber();
- } else {
- mNumberBackground = null;
- mNumberText = null;
- }
- invalidate();
- }
if (!visibilityEquals) {
setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
}
@@ -568,14 +542,6 @@
}
@Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- if (mNumberBackground != null) {
- placeNumber();
- }
- }
-
- @Override
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
updateDrawable();
@@ -609,10 +575,6 @@
canvas.restore();
}
- if (mNumberBackground != null) {
- mNumberBackground.draw(canvas);
- canvas.drawText(mNumberText, mNumberX, mNumberY, mNumberPain);
- }
if (mDotAppearAmount != 0.0f) {
float radius;
float alpha = Color.alpha(mDecorColor) / 255.f;
@@ -640,39 +602,6 @@
Log.d("View", debugIndent(depth) + "icon=" + mIcon);
}
- void placeNumber() {
- final String str;
- final int tooBig = getContext().getResources().getInteger(
- android.R.integer.status_bar_notification_info_maxnum);
- if (mIcon.number > tooBig) {
- str = getContext().getResources().getString(
- android.R.string.status_bar_notification_info_overflow);
- } else {
- NumberFormat f = NumberFormat.getIntegerInstance();
- str = f.format(mIcon.number);
- }
- mNumberText = str;
-
- final int w = getWidth();
- final int h = getHeight();
- final Rect r = new Rect();
- mNumberPain.getTextBounds(str, 0, str.length(), r);
- final int tw = r.right - r.left;
- final int th = r.bottom - r.top;
- mNumberBackground.getPadding(r);
- int dw = r.left + tw + r.right;
- if (dw < mNumberBackground.getMinimumWidth()) {
- dw = mNumberBackground.getMinimumWidth();
- }
- mNumberX = w-r.right-((dw-r.right-r.left)/2);
- int dh = r.top + th + r.bottom;
- if (dh < mNumberBackground.getMinimumWidth()) {
- dh = mNumberBackground.getMinimumWidth();
- }
- mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2);
- mNumberBackground.setBounds(w-dw, h-dh, w, h);
- }
-
@Override
public String toString() {
return "StatusBarIconView("
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 79218ae..89b7ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -53,6 +53,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
@@ -114,6 +115,7 @@
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private final Lazy<DeviceUnlockedInteractor> mDeviceUnlockedInteractorLazy;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
+ private final Lazy<SceneContainerOcclusionInteractor> mSceneContainerOcclusionInteractorLazy;
private final Lazy<KeyguardClockInteractor> mKeyguardClockInteractorLazy;
private int mState;
private int mLastState;
@@ -182,6 +184,7 @@
Lazy<ShadeInteractor> shadeInteractorLazy,
Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
Lazy<SceneInteractor> sceneInteractorLazy,
+ Lazy<SceneContainerOcclusionInteractor> sceneContainerOcclusionInteractor,
Lazy<KeyguardClockInteractor> keyguardClockInteractorLazy) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitorLazy = interactionJankMonitorLazy;
@@ -190,6 +193,7 @@
mShadeInteractorLazy = shadeInteractorLazy;
mDeviceUnlockedInteractorLazy = deviceUnlockedInteractorLazy;
mSceneInteractorLazy = sceneInteractorLazy;
+ mSceneContainerOcclusionInteractorLazy = sceneContainerOcclusionInteractor;
mKeyguardClockInteractorLazy = keyguardClockInteractorLazy;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
@@ -214,6 +218,7 @@
combineFlows(
mDeviceUnlockedInteractorLazy.get().getDeviceUnlockStatus(),
mSceneInteractorLazy.get().getCurrentScene(),
+ mSceneContainerOcclusionInteractorLazy.get().getInvisibleDueToOcclusion(),
this::calculateStateFromSceneFramework),
this::onStatusBarStateChanged);
}
@@ -664,10 +669,11 @@
private int calculateStateFromSceneFramework(
DeviceUnlockStatus deviceUnlockStatus,
- SceneKey currentScene) {
+ SceneKey currentScene,
+ boolean isOccluded) {
SceneContainerFlag.isUnexpectedlyInLegacyMode();
- if (deviceUnlockStatus.isUnlocked()) {
+ if (deviceUnlockStatus.isUnlocked() || isOccluded) {
return StatusBarState.SHADE;
} else {
return Preconditions.checkNotNull(sStatusBarStateByLockedSceneKey.get(currentScene));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index ad09aa3..ed1756a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -16,14 +16,17 @@
package com.android.systemui.statusbar.chips.call.ui.viewmodel
+import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
@@ -52,40 +55,58 @@
when (state) {
is OngoingCallModel.NoCall -> OngoingActivityChipModel.Hidden
is OngoingCallModel.InCall -> {
- // This mimics OngoingCallController#updateChip.
- // TODO(b/332662551): Handle `state.startTimeMs = 0` correctly (see
- // b/192379214 and
- // OngoingCallController.CallNotificationInfo.hasValidStartTime).
- val startTimeInElapsedRealtime =
- state.startTimeMs - systemClock.currentTimeMillis() +
- systemClock.elapsedRealtime()
- OngoingActivityChipModel.Shown(
- icon =
- Icon.Resource(
- com.android.internal.R.drawable.ic_phone,
- contentDescription = null,
- ),
- startTimeMs = startTimeInElapsedRealtime,
- ) {
- if (state.intent != null) {
- val backgroundView =
- it.requireViewById<ChipBackgroundContainer>(
- R.id.ongoing_activity_chip_background
- )
- // TODO(b/332662551): Log the click event.
- // This mimics OngoingCallController#updateChipClickListener.
- activityStarter.postStartActivityDismissingKeyguard(
- state.intent,
- ActivityTransitionAnimator.Controller.fromView(
- backgroundView,
- InteractionJankMonitor
- .CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
- )
- )
- }
+ // This block mimics OngoingCallController#updateChip.
+ if (state.startTimeMs <= 0L) {
+ // If the start time is invalid, don't show a timer and show just an
+ // icon. See b/192379214.
+ OngoingActivityChipModel.Shown.IconOnly(
+ icon = phoneIcon,
+ colors = ColorsModel.Themed,
+ getOnClickListener(state),
+ )
+ } else {
+ val startTimeInElapsedRealtime =
+ state.startTimeMs - systemClock.currentTimeMillis() +
+ systemClock.elapsedRealtime()
+ OngoingActivityChipModel.Shown.Timer(
+ icon = phoneIcon,
+ colors = ColorsModel.Themed,
+ startTimeMs = startTimeInElapsedRealtime,
+ getOnClickListener(state),
+ )
}
}
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+
+ private fun getOnClickListener(state: OngoingCallModel.InCall): View.OnClickListener? {
+ if (state.intent == null) {
+ return null
+ }
+
+ return View.OnClickListener { view ->
+ val backgroundView =
+ view.requireViewById<ChipBackgroundContainer>(R.id.ongoing_activity_chip_background)
+ // TODO(b/332662551): Log the click event.
+ // This mimics OngoingCallController#updateChipClickListener.
+ activityStarter.postStartActivityDismissingKeyguard(
+ state.intent,
+ ActivityTransitionAnimator.Controller.fromView(
+ backgroundView,
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ )
+ )
+ }
+ }
+
+ companion object {
+ private val phoneIcon =
+ Icon.Resource(
+ com.android.internal.R.drawable.ic_phone,
+ ContentDescription.Resource(
+ R.string.ongoing_phone_call_content_description,
+ ),
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index a1678bf..73ccaab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
@@ -69,7 +70,8 @@
}
}
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+ // See b/347726238.
+ .stateIn(scope, SharingStarted.Lazily, OngoingActivityChipModel.Hidden)
/** Stops the currently active projection. */
private fun stopProjecting() {
@@ -79,12 +81,19 @@
private fun createCastToOtherDeviceChip(
state: ProjectionChipModel.Projecting,
): OngoingActivityChipModel.Shown {
- return OngoingActivityChipModel.Shown(
+ return OngoingActivityChipModel.Shown.Timer(
icon =
Icon.Resource(
CAST_TO_OTHER_DEVICE_ICON,
- ContentDescription.Resource(R.string.accessibility_casting),
+ // Note: This string is "Casting screen", which is okay right now because this
+ // chip does not currently support audio-only casting. If the chip starts
+ // supporting audio-only casting (see b/342169876), update the content
+ // description to just "Casting".
+ ContentDescription.Resource(
+ R.string.cast_to_other_device_chip_accessibility_label,
+ ),
),
+ colors = ColorsModel.Red,
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
startTimeMs = systemClock.elapsedRealtime(),
createDialogLaunchOnClickListener(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 1e9f0a1..43b1d16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -50,7 +50,6 @@
) { screenRecordState, mediaProjectionState ->
when (screenRecordState) {
is ScreenRecordModel.DoingNothing -> ScreenRecordChipModel.DoingNothing
- // TODO(b/332662551): Implement the 3-2-1 countdown chip.
is ScreenRecordModel.Starting ->
ScreenRecordChipModel.Starting(screenRecordState.millisUntilStarted)
is ScreenRecordModel.Recording -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index d190cfd..53679f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -19,14 +19,17 @@
import android.app.ActivityManager
import androidx.annotation.DrawableRes
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel.Starting.Companion.toCountdownSeconds
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
@@ -54,12 +57,22 @@
.map { state ->
when (state) {
is ScreenRecordChipModel.DoingNothing -> OngoingActivityChipModel.Hidden
- // TODO(b/332662551): Implement the 3-2-1 countdown chip.
- is ScreenRecordChipModel.Starting -> OngoingActivityChipModel.Hidden
+ is ScreenRecordChipModel.Starting -> {
+ OngoingActivityChipModel.Shown.Countdown(
+ colors = ColorsModel.Red,
+ secondsUntilStarted = state.millisUntilStarted.toCountdownSeconds(),
+ )
+ }
is ScreenRecordChipModel.Recording -> {
- OngoingActivityChipModel.Shown(
- // TODO(b/332662551): Also provide a content description.
- icon = Icon.Resource(ICON, contentDescription = null),
+ OngoingActivityChipModel.Shown.Timer(
+ icon =
+ Icon.Resource(
+ ICON,
+ ContentDescription.Resource(
+ R.string.screenrecord_ongoing_screen_only,
+ ),
+ ),
+ colors = ColorsModel.Red,
startTimeMs = systemClock.elapsedRealtime(),
createDialogLaunchOnClickListener(
createDelegate(state.recordedTask),
@@ -69,7 +82,8 @@
}
}
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+ // See b/347726238.
+ .stateIn(scope, SharingStarted.Lazily, OngoingActivityChipModel.Hidden)
private fun createDelegate(
recordedTask: ActivityManager.RunningTaskInfo?
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index dc41002..8aef5a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -18,6 +18,7 @@
import androidx.annotation.DrawableRes
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -26,6 +27,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
@@ -65,7 +67,8 @@
}
}
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+ // See b/347726238.
+ .stateIn(scope, SharingStarted.Lazily, OngoingActivityChipModel.Hidden)
/** Stops the currently active projection. */
private fun stopProjecting() {
@@ -75,9 +78,13 @@
private fun createShareToAppChip(
state: ProjectionChipModel.Projecting,
): OngoingActivityChipModel.Shown {
- return OngoingActivityChipModel.Shown(
- // TODO(b/332662551): Use the right content description.
- icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null),
+ return OngoingActivityChipModel.Shown.Timer(
+ icon =
+ Icon.Resource(
+ SHARE_TO_APP_ICON,
+ ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label),
+ ),
+ colors = ColorsModel.Red,
// TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
startTimeMs = systemClock.elapsedRealtime(),
createDialogLaunchOnClickListener(
@@ -95,7 +102,6 @@
)
companion object {
- // TODO(b/332662551): Use the right icon.
- @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_screenshot_share
+ @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_present_to_all
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
new file mode 100644
index 0000000..b2140f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.ui.model
+
+import android.content.Context
+import android.content.res.ColorStateList
+import androidx.annotation.ColorInt
+import com.android.settingslib.Utils
+import com.android.systemui.res.R
+
+/** Model representing how the chip in the status bar should be colored. */
+sealed interface ColorsModel {
+ /** The color for the background of the chip. */
+ fun background(context: Context): ColorStateList
+
+ /** The color for the text (and icon) on the chip. */
+ @ColorInt fun text(context: Context): Int
+
+ /** The chip should match the theme's primary color. */
+ data object Themed : ColorsModel {
+ override fun background(context: Context): ColorStateList =
+ Utils.getColorAttr(context, com.android.internal.R.attr.colorAccent)
+
+ override fun text(context: Context) =
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ }
+
+ /** The chip should have a red background with white text. */
+ data object Red : ColorsModel {
+ override fun background(context: Context): ColorStateList =
+ ColorStateList.valueOf(context.getColor(R.color.GM2_red_600))
+
+ override fun text(context: Context) = context.getColor(android.R.color.white)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index e63713b..e8d895c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -25,20 +25,50 @@
data object Hidden : OngoingActivityChipModel()
/** This chip should be shown with the given information. */
- data class Shown(
- /** The icon to show on the chip. */
- val icon: Icon,
+ abstract class Shown(
+ /** The icon to show on the chip. If null, no icon will be shown. */
+ open val icon: Icon?,
+ /** What colors to use for the chip. */
+ open val colors: ColorsModel,
/**
- * The time this event started, used to show the timer.
- *
- * This time should be relative to
- * [com.android.systemui.util.time.SystemClock.elapsedRealtime], *not*
- * [com.android.systemui.util.time.SystemClock.currentTimeMillis] because the
- * [ChipChronometer] is based off of elapsed realtime. See
- * [android.widget.Chronometer.setBase].
+ * Listener method to invoke when this chip is clicked. If null, the chip won't be
+ * clickable.
*/
- val startTimeMs: Long,
- /** Listener method to invoke when this chip is clicked. */
- val onClickListener: View.OnClickListener,
- ) : OngoingActivityChipModel()
+ open val onClickListener: View.OnClickListener?,
+ ) : OngoingActivityChipModel() {
+
+ /** This chip shows only an icon and nothing else. */
+ data class IconOnly(
+ override val icon: Icon,
+ override val colors: ColorsModel,
+ override val onClickListener: View.OnClickListener?,
+ ) : Shown(icon, colors, onClickListener)
+
+ /** The chip shows a timer, counting up from [startTimeMs]. */
+ data class Timer(
+ override val icon: Icon,
+ override val colors: ColorsModel,
+ /**
+ * The time this event started, used to show the timer.
+ *
+ * This time should be relative to
+ * [com.android.systemui.util.time.SystemClock.elapsedRealtime], *not*
+ * [com.android.systemui.util.time.SystemClock.currentTimeMillis] because the
+ * [ChipChronometer] is based off of elapsed realtime. See
+ * [android.widget.Chronometer.setBase].
+ */
+ val startTimeMs: Long,
+ override val onClickListener: View.OnClickListener?,
+ ) : Shown(icon, colors, onClickListener)
+
+ /**
+ * This chip shows a countdown using [secondsUntilStarted]. Used to inform users that an
+ * event is about to start. Typically, a [Countdown] chip will turn into a [Timer] chip.
+ */
+ data class Countdown(
+ override val colors: ColorsModel,
+ /** The number of seconds until an event is started. */
+ val secondsUntilStarted: Long,
+ ) : Shown(icon = null, colors, onClickListener = null)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 1b79ce4..9c8086f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -72,5 +72,8 @@
else -> call
}
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+ // Some of the chips could have timers in them and we don't want the start time
+ // for those timers to get reset for any reason. So, as soon as any subscriber has
+ // requested the chip information, we need to maintain it forever. See b/347726238.
+ .stateIn(scope, SharingStarted.Lazily, OngoingActivityChipModel.Hidden)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
index 933d0ab..b467032 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarMode.kt
@@ -16,13 +16,13 @@
package com.android.systemui.statusbar.data.model
-import com.android.systemui.statusbar.phone.BarTransitions
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT
-import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode
+import com.android.systemui.shared.statusbar.phone.BarTransitions
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode
/**
* The possible status bar modes.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index af8a89d..ef4dffad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -305,26 +305,20 @@
dumpManager.registerDumpable(this)
}
- fun isEnabled(): Boolean {
- execution.assertIsMainThread()
+ val isEnabled: Boolean = plugin != null
- return plugin != null
- }
+ val isDateWeatherDecoupled: Boolean = datePlugin != null && weatherPlugin != null
- fun isDateWeatherDecoupled(): Boolean {
- execution.assertIsMainThread()
-
- return datePlugin != null && weatherPlugin != null
- }
-
- fun isWeatherEnabled(): Boolean {
- execution.assertIsMainThread()
- val showWeather = secureSettings.getIntForUser(
- LOCK_SCREEN_WEATHER_ENABLED,
- 1,
- userTracker.userId) == 1
- return showWeather
- }
+ val isWeatherEnabled: Boolean
+ get() {
+ val showWeather =
+ secureSettings.getIntForUser(
+ LOCK_SCREEN_WEATHER_ENABLED,
+ 1,
+ userTracker.userId,
+ ) == 1
+ return showWeather
+ }
private fun updateBypassEnabled() {
val bypassEnabled = bypassController.bypassEnabled
@@ -337,10 +331,10 @@
fun buildAndConnectDateView(parent: ViewGroup): View? {
execution.assertIsMainThread()
- if (!isEnabled()) {
+ if (!isEnabled) {
throw RuntimeException("Cannot build view when not enabled")
}
- if (!isDateWeatherDecoupled()) {
+ if (!isDateWeatherDecoupled) {
throw RuntimeException("Cannot build date view when not decoupled")
}
@@ -361,10 +355,10 @@
fun buildAndConnectWeatherView(parent: ViewGroup): View? {
execution.assertIsMainThread()
- if (!isEnabled()) {
+ if (!isEnabled) {
throw RuntimeException("Cannot build view when not enabled")
}
- if (!isDateWeatherDecoupled()) {
+ if (!isDateWeatherDecoupled) {
throw RuntimeException("Cannot build weather view when not decoupled")
}
@@ -385,7 +379,7 @@
fun buildAndConnectView(parent: ViewGroup): View? {
execution.assertIsMainThread()
- if (!isEnabled()) {
+ if (!isEnabled) {
throw RuntimeException("Cannot build view when not enabled")
}
@@ -577,7 +571,7 @@
}
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
- if (isDateWeatherDecoupled() && t.featureType == SmartspaceTarget.FEATURE_WEATHER) {
+ if (isDateWeatherDecoupled && t.featureType == SmartspaceTarget.FEATURE_WEATHER) {
return false
}
if (!showNotifications) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 4c66f66..0bb18d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -58,7 +58,7 @@
constructor(
@Application applicationScope: CoroutineScope,
dumpManager: DumpManager,
- private val mHeadsUpManager: HeadsUpManager,
+ private val headsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
@@ -71,8 +71,8 @@
StatusBarStateController.StateListener,
ShadeExpansionListener,
Dumpable {
- private lateinit var mStackScrollerController: NotificationStackScrollLayoutController
- private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+ private lateinit var stackScrollerController: NotificationStackScrollLayoutController
+ private var visibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
private var inputLinearDozeAmount: Float = 0.0f
private var inputEasedDozeAmount: Float = 0.0f
@@ -85,13 +85,13 @@
private var outputEasedDozeAmount: Float = 0.0f
@VisibleForTesting val dozeAmountInterpolator: Interpolator = Interpolators.FAST_OUT_SLOW_IN
- private var mNotificationVisibleAmount = 0.0f
- private var mNotificationsVisible = false
- private var mNotificationsVisibleForExpansion = false
- private var mVisibilityAnimator: ObjectAnimator? = null
- private var mVisibilityAmount = 0.0f
- private var mLinearVisibilityAmount = 0.0f
- private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
+ private var notificationVisibleAmount = 0.0f
+ private var notificationsVisible = false
+ private var notificationsVisibleForExpansion = false
+ private var visibilityAnimator: ObjectAnimator? = null
+ private var visibilityAmount = 0.0f
+ private var linearVisibilityAmount = 0.0f
+ private val entrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
private var pulseExpanding: Boolean = false
private val wakeUpListeners = arrayListOf<WakeUpListener>()
private var state: Int = StatusBarState.KEYGUARD
@@ -104,14 +104,14 @@
willWakeUp = false
if (value) {
if (
- mNotificationsVisible &&
- !mNotificationsVisibleForExpansion &&
+ notificationsVisible &&
+ !notificationsVisibleForExpansion &&
!bypassController.bypassEnabled
) {
// We're waking up while pulsing, let's make sure the animation looks nice
- mStackScrollerController.wakeUpFromPulse()
+ stackScrollerController.wakeUpFromPulse()
}
- if (bypassController.bypassEnabled && !mNotificationsVisible) {
+ if (bypassController.bypassEnabled && !notificationsVisible) {
// Let's make sure our huns become visible once we are waking up in case
// they were blocked by the proximity sensor
updateNotificationVisibility(
@@ -186,13 +186,13 @@
init {
dumpManager.registerDumpable(this)
- mHeadsUpManager.addListener(this)
+ headsUpManager.addListener(this)
statusBarStateController.addCallback(this)
bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
addListener(
object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
- if (isFullyHidden && mNotificationsVisibleForExpansion) {
+ if (isFullyHidden && notificationsVisibleForExpansion) {
// When the notification becomes fully invisible, let's make sure our
// expansion
// flag also changes. This can happen if the bouncer shows when dragging
@@ -217,7 +217,7 @@
}
fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) {
- mStackScrollerController = stackScrollerController
+ this.stackScrollerController = stackScrollerController
pulseExpanding = stackScrollerController.isPulseExpanding
stackScrollerController.setOnPulseHeightChangedListener {
val nowExpanding = isPulseExpanding()
@@ -237,7 +237,7 @@
}
}
- fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding
+ fun isPulseExpanding(): Boolean = stackScrollerController.isPulseExpanding
/**
* @param visible should notifications be visible
@@ -249,13 +249,13 @@
animate: Boolean,
increaseSpeed: Boolean
) {
- mNotificationsVisibleForExpansion = visible
+ notificationsVisibleForExpansion = visible
updateNotificationVisibility(animate, increaseSpeed)
- if (!visible && mNotificationsVisible) {
+ if (!visible && notificationsVisible) {
// If we stopped expanding and we're still visible because we had a pulse that hasn't
// times out, let's release them all to make sure were not stuck in a state where
// notifications are visible
- mHeadsUpManager.releaseAllImmediately()
+ headsUpManager.releaseAllImmediately()
}
}
@@ -269,12 +269,12 @@
private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
// TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
- var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
+ var visible = notificationsVisibleForExpansion || headsUpManager.hasNotifications()
visible = visible && canShowPulsingHuns
if (
!visible &&
- mNotificationsVisible &&
+ notificationsVisible &&
(wakingUp || willWakeUp) &&
outputLinearDozeAmount != 0.0f
) {
@@ -290,11 +290,11 @@
animate: Boolean,
increaseSpeed: Boolean
) {
- if (mNotificationsVisible == visible) {
+ if (notificationsVisible == visible) {
return
}
- mNotificationsVisible = visible
- mVisibilityAnimator?.cancel()
+ notificationsVisible = visible
+ visibilityAnimator?.cancel()
if (animate) {
notifyAnimationStart(visible)
startVisibilityAnimation(increaseSpeed)
@@ -371,7 +371,7 @@
state = statusBarStateController.state,
changed = changed
)
- mStackScrollerController.setDozeAmount(outputEasedDozeAmount)
+ stackScrollerController.setDozeAmount(outputEasedDozeAmount)
updateHideAmount()
if (changed && outputLinearDozeAmount == 0.0f) {
setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
@@ -475,7 +475,7 @@
this.collapsedEnoughToHide = collapsedEnough
if (couldShowPulsingHuns && !canShowPulsingHuns) {
updateNotificationVisibility(animate = true, increaseSpeed = true)
- mHeadsUpManager.releaseAllImmediately()
+ headsUpManager.releaseAllImmediately()
}
}
}
@@ -562,12 +562,12 @@
}
private fun startVisibilityAnimation(increaseSpeed: Boolean) {
- if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
- mVisibilityInterpolator =
- if (mNotificationsVisible) Interpolators.TOUCH_RESPONSE
+ if (notificationVisibleAmount == 0f || notificationVisibleAmount == 1f) {
+ visibilityInterpolator =
+ if (notificationsVisible) Interpolators.TOUCH_RESPONSE
else Interpolators.FAST_OUT_SLOW_IN_REVERSE
}
- val target = if (mNotificationsVisible) 1.0f else 0.0f
+ val target = if (notificationsVisible) 1.0f else 0.0f
val visibilityAnimator = ObjectAnimator.ofFloat(this, notificationVisibility, target)
visibilityAnimator.interpolator = InterpolatorsAndroidX.LINEAR
var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
@@ -576,34 +576,34 @@
}
visibilityAnimator.duration = duration
visibilityAnimator.start()
- mVisibilityAnimator = visibilityAnimator
+ this.visibilityAnimator = visibilityAnimator
}
private fun setVisibilityAmount(visibilityAmount: Float) {
logger.logSetVisibilityAmount(visibilityAmount)
- mLinearVisibilityAmount = visibilityAmount
- mVisibilityAmount = mVisibilityInterpolator.getInterpolation(visibilityAmount)
+ linearVisibilityAmount = visibilityAmount
+ this.visibilityAmount = visibilityInterpolator.getInterpolation(visibilityAmount)
handleAnimationFinished()
updateHideAmount()
}
private fun handleAnimationFinished() {
- if (outputLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
- mEntrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
- mEntrySetToClearWhenFinished.clear()
+ if (outputLinearDozeAmount == 0.0f || linearVisibilityAmount == 0.0f) {
+ entrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
+ entrySetToClearWhenFinished.clear()
}
}
private fun updateHideAmount() {
- val linearAmount = min(1.0f - mLinearVisibilityAmount, outputLinearDozeAmount)
- val amount = min(1.0f - mVisibilityAmount, outputEasedDozeAmount)
+ val linearAmount = min(1.0f - linearVisibilityAmount, outputLinearDozeAmount)
+ val amount = min(1.0f - visibilityAmount, outputEasedDozeAmount)
logger.logSetHideAmount(linearAmount)
- mStackScrollerController.setHideAmount(linearAmount, amount)
+ stackScrollerController.setHideAmount(linearAmount, amount)
notificationsFullyHidden = linearAmount == 1.0f
}
private fun notifyAnimationStart(awake: Boolean) {
- mStackScrollerController.notifyHideAnimationStart(!awake)
+ stackScrollerController.notifyHideAnimationStart(!awake)
}
override fun onDozingChanged(isDozing: Boolean) {
@@ -615,7 +615,7 @@
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
var animate = shouldAnimateVisibility()
if (!isHeadsUp) {
- if (outputLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
+ if (outputLinearDozeAmount != 0.0f && linearVisibilityAmount != 0.0f) {
if (entry.isRowDismissed) {
// if we animate, we see the shelf briefly visible. Instead we fully animate
// the notification and its background out
@@ -623,11 +623,11 @@
} else if (!wakingUp && !willWakeUp) {
// TODO: look that this is done properly and not by anyone else
entry.setHeadsUpAnimatingAway(true)
- mEntrySetToClearWhenFinished.add(entry)
+ entrySetToClearWhenFinished.add(entry)
}
}
- } else if (mEntrySetToClearWhenFinished.contains(entry)) {
- mEntrySetToClearWhenFinished.remove(entry)
+ } else if (entrySetToClearWhenFinished.contains(entry)) {
+ entrySetToClearWhenFinished.remove(entry)
entry.setHeadsUpAnimatingAway(false)
}
updateNotificationVisibility(animate, increaseSpeed = false)
@@ -644,11 +644,11 @@
pw.println("hardDozeAmountOverrideSource: $hardDozeAmountOverrideSource")
pw.println("outputLinearDozeAmount: $outputLinearDozeAmount")
pw.println("outputEasedDozeAmount: $outputEasedDozeAmount")
- pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
- pw.println("mNotificationsVisible: $mNotificationsVisible")
- pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
- pw.println("mVisibilityAmount: $mVisibilityAmount")
- pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+ pw.println("notificationVisibleAmount: $notificationVisibleAmount")
+ pw.println("notificationsVisible: $notificationsVisible")
+ pw.println("notificationsVisibleForExpansion: $notificationsVisibleForExpansion")
+ pw.println("visibilityAmount: $visibilityAmount")
+ pw.println("linearVisibilityAmount: $linearVisibilityAmount")
pw.println("pulseExpanding: $pulseExpanding")
pw.println("state: ${StatusBarState.toString(state)}")
pw.println("fullyAwake: $fullyAwake")
@@ -698,7 +698,7 @@
}
override fun get(coordinator: NotificationWakeUpCoordinator): Float {
- return coordinator.mLinearVisibilityAmount
+ return coordinator.linearVisibilityAmount
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 1adfef0..f98a88f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -68,9 +68,11 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository;
import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel;
import com.android.systemui.statusbar.notification.stack.PriorityBucket;
import com.android.systemui.util.ListenerSet;
@@ -97,7 +99,7 @@
* At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
* clean this up in the future.
*/
-public final class NotificationEntry extends ListEntry {
+public final class NotificationEntry extends ListEntry implements NotificationRowRepository {
private final String mKey;
private StatusBarNotification mSbn;
@@ -159,6 +161,8 @@
StateFlowKt.MutableStateFlow(null);
private final MutableStateFlow<CharSequence> mHeadsUpStatusBarTextPublic =
StateFlowKt.MutableStateFlow(null);
+ private final MutableStateFlow<RichOngoingContentModel> mRichOngoingContentModel =
+ StateFlowKt.MutableStateFlow(null);
// indicates when this entry's view was first attached to a window
// this value will reset when the view is completely removed from the shade (ie: filtered out)
@@ -945,6 +949,7 @@
}
/** @see #setHeadsUpStatusBarText(CharSequence) */
+ @NonNull
public StateFlow<CharSequence> getHeadsUpStatusBarText() {
return mHeadsUpStatusBarText;
}
@@ -959,10 +964,17 @@
}
/** @see #setHeadsUpStatusBarTextPublic(CharSequence) */
+ @NonNull
public StateFlow<CharSequence> getHeadsUpStatusBarTextPublic() {
return mHeadsUpStatusBarTextPublic;
}
+ /** Gets the current RON content model, which may be null */
+ @NonNull
+ public StateFlow<RichOngoingContentModel> getRichOngoingContentModel() {
+ return mRichOngoingContentModel;
+ }
+
/**
* Sets the text to be displayed on the StatusBar, when this notification is the top pinned
* heads up, and its content is sensitive right now.
@@ -1047,6 +1059,7 @@
HeadsUpStatusBarModel headsUpStatusBarModel = contentModel.getHeadsUpStatusBarModel();
this.mHeadsUpStatusBarText.setValue(headsUpStatusBarModel.getPrivateText());
this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
+ this.mRichOngoingContentModel.setValue(contentModel.getRichOngoingContentModel());
}
/** Information about a suggestion that is being edited. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProvider.kt
index 5adf31b..f166d32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProvider.kt
@@ -13,12 +13,18 @@
/** The subset of active listeners which are temporary (will be removed after called) */
private val temporaryListeners = ArraySet<OnReorderingAllowedListener>()
+ private val banListeners = ListenerSet<OnReorderingBannedListener>()
+
var isReorderingAllowed = true
set(value) {
if (field != value) {
field = value
if (value) {
notifyReorderingAllowed()
+ } else {
+ banListeners.forEach { listener ->
+ listener.onReorderingBanned()
+ }
}
}
}
@@ -38,6 +44,10 @@
allListeners.addIfAbsent(listener)
}
+ fun addPersistentReorderingBannedListener(listener: OnReorderingBannedListener) {
+ banListeners.addIfAbsent(listener)
+ }
+
/** Add a listener which will be removed when it is called. */
fun addTemporaryReorderingAllowedListener(listener: OnReorderingAllowedListener) {
// Only add to the temporary set if it was added to the global set
@@ -57,3 +67,8 @@
fun interface OnReorderingAllowedListener {
fun onReorderingAllowed()
}
+
+fun interface OnReorderingBannedListener {
+ fun onReorderingBanned()
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 91bb28e..762c9a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -21,8 +21,8 @@
import android.service.notification.NotificationListenerService;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository;
-import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepositoryImpl;
+import com.android.settingslib.statusbar.notification.data.repository.ZenModeRepository;
+import com.android.settingslib.statusbar.notification.data.repository.ZenModeRepositoryImpl;
import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
@@ -279,19 +279,19 @@
@Provides
@SysUISingleton
- public static NotificationsSoundPolicyRepository provideNotificationsSoundPolicyRepository(
+ static ZenModeRepository provideZenModeRepository(
Context context,
NotificationManager notificationManager,
@Application CoroutineScope coroutineScope,
@Background CoroutineContext coroutineContext) {
- return new NotificationsSoundPolicyRepositoryImpl(context, notificationManager,
+ return new ZenModeRepositoryImpl(context, notificationManager,
coroutineScope, coroutineContext);
}
@Provides
@SysUISingleton
- public static NotificationsSoundPolicyInteractor provideNotificationsSoundPolicyInteractror(
- NotificationsSoundPolicyRepository repository) {
+ static NotificationsSoundPolicyInteractor provideNotificationsSoundPolicyInteractor(
+ ZenModeRepository repository) {
return new NotificationsSoundPolicyInteractor(repository);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 190a2cd..f73223f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -111,6 +111,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
@@ -328,38 +329,61 @@
private OnClickListener mExpandClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
- if (!shouldShowPublic() && (!mIsMinimized || isExpanded())
- && mGroupMembershipManager.isGroupSummary(mEntry)) {
- mGroupExpansionChanging = true;
- final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
- boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ toggleExpansionState(v, /* shouldLogExpandClickMetric = */true);
+ }
+ };
+
+ /**
+ * Toggles expansion state.
+ */
+ public void toggleExpansionState() {
+ toggleExpansionState(this, /*shouldLogExpandClickMetric*/ false);
+ }
+
+ private void toggleExpansionState(View v, boolean shouldLogExpandClickMetric) {
+ if (!shouldShowPublic() && (!mIsMinimized || isExpanded())
+ && mGroupMembershipManager.isGroupSummary(mEntry)) {
+ mGroupExpansionChanging = true;
+ final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
+ boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ if (shouldLogExpandClickMetric) {
mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded);
- onExpansionChanged(true /* userAction */, wasExpanded);
- } else if (mEnableNonGroupedNotificationExpand) {
- if (v.isAccessibilityFocused()) {
- mPrivateLayout.setFocusOnVisibilityChange();
+ }
+ onExpansionChanged(true /* userAction */, wasExpanded);
+ } else if (mEnableNonGroupedNotificationExpand) {
+ if (v.isAccessibilityFocused()) {
+ mPrivateLayout.setFocusOnVisibilityChange();
+ }
+ boolean nowExpanded;
+ if (isPinned()) {
+ nowExpanded = !mExpandedWhenPinned;
+ mExpandedWhenPinned = nowExpanded;
+ // Also notify any expansion changed listeners. This is necessary since the
+ // expansion doesn't actually change (it's already system expanded) but it
+ // changes visually
+ if (mExpansionChangedListener != null) {
+ mExpansionChangedListener.onExpansionChanged(nowExpanded);
}
- boolean nowExpanded;
- if (isPinned()) {
- nowExpanded = !mExpandedWhenPinned;
- mExpandedWhenPinned = nowExpanded;
- // Also notify any expansion changed listeners. This is necessary since the
- // expansion doesn't actually change (it's already system expanded) but it
- // changes visually
- if (mExpansionChangedListener != null) {
- mExpansionChangedListener.onExpansionChanged(nowExpanded);
- }
- } else {
- nowExpanded = !isExpanded();
- setUserExpanded(nowExpanded);
- }
- notifyHeightChanged(/* needsAnimation= */ true);
- mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ } else {
+ nowExpanded = !isExpanded();
+ setUserExpanded(nowExpanded);
+ }
+
+ if (ExpandHeadsUpOnInlineReply.isEnabled() && mExpandable) {
+ // it is triggered by the user.
+ // So, mHasUserChangedExpansion should be marked true.
+ mHasUserChangedExpansion = true;
+ }
+
+ notifyHeightChanged(/* needsAnimation= */ true);
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
+ if (shouldLogExpandClickMetric) {
mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded);
}
}
- };
+ }
+
private boolean mKeepInParentForDismissAnimation;
private boolean mRemoved;
public static final FloatProperty<ExpandableNotificationRow> TRANSLATE_CONTENT =
@@ -2011,6 +2035,21 @@
};
}
+ /**
+ * Retrieves an OnClickListener for the close button of a notification, which when invoked,
+ * dismisses the notificationc represented by the given ExpandableNotificationRow.
+ *
+ * @param row The ExpandableNotificationRow representing the notification to be dismissed.
+ * @return An OnClickListener instance that dismisses the notification(s) when invoked.
+ */
+ public View.OnClickListener getCloseButtonOnClickListener(ExpandableNotificationRow row) {
+ return v -> {
+ if (row != null) {
+ row.performDismiss(false);
+ }
+ };
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Trace.beginSection(appendTraceStyleTag("ExpNotRow#onMeasure"));
@@ -2830,9 +2869,16 @@
}
public boolean isExpanded(boolean allowOnKeyguard) {
+ // System expanded should be ignored in heads up state
+ final boolean isHeadsUpState = ExpandHeadsUpOnInlineReply.isEnabled()
+ && canShowHeadsUp() && isHeadsUpState();
+ // Heads Up Notification can be expanded when it is pinned.
+ final boolean isPinnedAndExpanded =
+ isHeadsUpState && isPinnedAndExpanded();
return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
- && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
- || isUserExpanded());
+ && (!hasUserChangedExpansion() && !isHeadsUpState
+ && (isSystemExpanded() || isSystemChildExpanded())
+ || isUserExpanded() || isPinnedAndExpanded);
}
private boolean isSystemChildExpanded() {
@@ -2935,24 +2981,21 @@
if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) {
return;
}
- float oldAlpha = getContentView().getAlpha();
if (!animated) {
- mPublicLayout.animate().cancel();
- mPrivateLayout.animate().cancel();
- if (mChildrenContainer != null) {
- mChildrenContainer.animate().cancel();
+ if (!NotificationContentAlphaOptimization.isEnabled()
+ || mShowingPublic != oldShowingPublic) {
+ // Don't reset the alpha or cancel the animation if the showing layout doesn't
+ // change
+ mPublicLayout.animate().cancel();
+ mPrivateLayout.animate().cancel();
+ if (mChildrenContainer != null) {
+ mChildrenContainer.animate().cancel();
+ }
+ resetAllContentAlphas();
}
- resetAllContentAlphas();
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
- if (NotificationContentAlphaOptimization.isEnabled()) {
- // We want to set the old alpha to the now-showing layout to avoid breaking an
- // on-going animation
- if (oldAlpha != 1f) {
- setAlphaAndLayerType(mShowingPublic ? mPublicLayout : mPrivateLayout, oldAlpha);
- }
- }
} else {
animateShowingPublic(delay, duration, mShowingPublic);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 6f00d96..646d0b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -72,6 +73,8 @@
import com.android.systemui.util.Compile;
import com.android.systemui.util.DumpUtilsKt;
+import kotlinx.coroutines.DisposableHandle;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -109,6 +112,8 @@
private View mHeadsUpChild;
private HybridNotificationView mSingleLineView;
+ @Nullable public DisposableHandle mContractedBinderHandle;
+
private RemoteInputView mExpandedRemoteInput;
private RemoteInputView mHeadsUpRemoteInput;
@@ -909,7 +914,9 @@
View visibleView = getViewForVisibleType(visibleType);
if (visibleView != null) {
visibleView.setVisibility(VISIBLE);
- transferRemoteInputFocus(visibleType);
+ if (!ExpandHeadsUpOnInlineReply.isEnabled()) {
+ transferRemoteInputFocus(visibleType);
+ }
}
if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
@@ -1393,30 +1400,39 @@
mCachedExpandedRemoteInput = null;
mCachedExpandedRemoteInputViewController = null;
- if (mHeadsUpChild != null) {
- RemoteInputViewData headsUpData = applyRemoteInput(mHeadsUpChild, mNotificationEntry,
- hasFreeformRemoteInput, mPreviousHeadsUpRemoteInputIntent,
- mCachedHeadsUpRemoteInput, mCachedHeadsUpRemoteInputViewController,
- mHeadsUpWrapper);
- mHeadsUpRemoteInput = headsUpData.mView;
- mHeadsUpRemoteInputController = headsUpData.mController;
- if (mHeadsUpRemoteInputController != null) {
- mHeadsUpRemoteInputController.bind();
- }
- } else {
+ if (ExpandHeadsUpOnInlineReply.isEnabled()) {
mHeadsUpRemoteInput = null;
- if (mHeadsUpRemoteInputController != null) {
- mHeadsUpRemoteInputController.unbind();
- }
mHeadsUpRemoteInputController = null;
+ mCachedHeadsUpRemoteInput = null;
+ mCachedHeadsUpRemoteInputViewController = null;
+ } else {
+ ExpandHeadsUpOnInlineReply.assertInLegacyMode();
+ if (mHeadsUpChild != null) {
+ RemoteInputViewData headsUpData = applyRemoteInput(mHeadsUpChild,
+ mNotificationEntry,
+ hasFreeformRemoteInput, mPreviousHeadsUpRemoteInputIntent,
+ mCachedHeadsUpRemoteInput, mCachedHeadsUpRemoteInputViewController,
+ mHeadsUpWrapper);
+ mHeadsUpRemoteInput = headsUpData.mView;
+ mHeadsUpRemoteInputController = headsUpData.mController;
+ if (mHeadsUpRemoteInputController != null) {
+ mHeadsUpRemoteInputController.bind();
+ }
+ } else {
+ mHeadsUpRemoteInput = null;
+ if (mHeadsUpRemoteInputController != null) {
+ mHeadsUpRemoteInputController.unbind();
+ }
+ mHeadsUpRemoteInputController = null;
+ }
+ if (mCachedHeadsUpRemoteInput != null
+ && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
+ // We had a cached remote input but didn't reuse it. Clean up required.
+ mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
+ }
+ mCachedHeadsUpRemoteInput = null;
+ mCachedHeadsUpRemoteInputViewController = null;
}
- if (mCachedHeadsUpRemoteInput != null
- && mCachedHeadsUpRemoteInput != mHeadsUpRemoteInput) {
- // We had a cached remote input but didn't reuse it. Clean up required.
- mCachedHeadsUpRemoteInput.dispatchFinishTemporaryDetach();
- }
- mCachedHeadsUpRemoteInput = null;
- mCachedHeadsUpRemoteInputViewController = null;
}
private RemoteInputViewData applyRemoteInput(View view, NotificationEntry entry,
@@ -1461,7 +1477,7 @@
}
if (hasRemoteInput) {
result.mView.setWrapper(wrapper);
- result.mView.addOnVisibilityChangedListener(this::setRemoteInputVisible);
+ result.mView.setOnVisibilityChangedListener(this::setRemoteInputVisible);
if (existingPendingIntent != null || result.mView.isActive()) {
// The current action could be gone, or the pending intent no longer valid.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index e704140..492d802 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -46,6 +46,10 @@
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.InflationException
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
+import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP
+import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_SINGLELINE
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
@@ -62,6 +66,7 @@
import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder
import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper
@@ -86,6 +91,8 @@
private val remoteViewCache: NotifRemoteViewCache,
private val remoteInputManager: NotificationRemoteInputManager,
private val conversationProcessor: ConversationNotificationProcessor,
+ private val ronExtractor: RichOngoingNotificationContentExtractor,
+ private val ronInflater: RichOngoingNotificationViewInflater,
@NotifInflation private val inflationExecutor: Executor,
private val smartReplyStateInflater: SmartReplyStateInflater,
private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
@@ -133,6 +140,8 @@
remoteViewCache,
entry,
conversationProcessor,
+ ronExtractor,
+ ronInflater,
row,
bindParams.isMinimized,
bindParams.usesIncreasedHeight,
@@ -177,6 +186,7 @@
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
conversationProcessor = conversationProcessor,
+ ronExtractor = ronExtractor,
logger = logger,
)
inflateSmartReplyViews(
@@ -255,39 +265,31 @@
) {
when (inflateFlag) {
FLAG_CONTENT_VIEW_CONTRACTED ->
- row.privateLayout.performWhenContentInactive(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED
- ) {
+ row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_CONTRACTED) {
+ row.privateLayout.mContractedBinderHandle?.dispose()
+ row.privateLayout.mContractedBinderHandle = null
row.privateLayout.setContractedChild(null)
remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
}
FLAG_CONTENT_VIEW_EXPANDED ->
- row.privateLayout.performWhenContentInactive(
- NotificationContentView.VISIBLE_TYPE_EXPANDED
- ) {
+ row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_EXPANDED) {
row.privateLayout.setExpandedChild(null)
remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
}
FLAG_CONTENT_VIEW_HEADS_UP ->
- row.privateLayout.performWhenContentInactive(
- NotificationContentView.VISIBLE_TYPE_HEADSUP
- ) {
+ row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_HEADSUP) {
row.privateLayout.setHeadsUpChild(null)
remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
row.privateLayout.setHeadsUpInflatedSmartReplies(null)
}
FLAG_CONTENT_VIEW_PUBLIC ->
- row.publicLayout.performWhenContentInactive(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED
- ) {
+ row.publicLayout.performWhenContentInactive(VISIBLE_TYPE_CONTRACTED) {
row.publicLayout.setContractedChild(null)
remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)
}
FLAG_CONTENT_VIEW_SINGLE_LINE -> {
if (AsyncHybridViewInflation.isEnabled) {
- row.privateLayout.performWhenContentInactive(
- NotificationContentView.VISIBLE_TYPE_SINGLELINE
- ) {
+ row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_SINGLELINE) {
row.privateLayout.setSingleLineView(null)
}
}
@@ -308,32 +310,22 @@
@InflationFlag contentViews: Int
) {
if (contentViews and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
- row.privateLayout.removeContentInactiveRunnable(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED
- )
+ row.privateLayout.removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED)
}
if (contentViews and FLAG_CONTENT_VIEW_EXPANDED != 0) {
- row.privateLayout.removeContentInactiveRunnable(
- NotificationContentView.VISIBLE_TYPE_EXPANDED
- )
+ row.privateLayout.removeContentInactiveRunnable(VISIBLE_TYPE_EXPANDED)
}
if (contentViews and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
- row.privateLayout.removeContentInactiveRunnable(
- NotificationContentView.VISIBLE_TYPE_HEADSUP
- )
+ row.privateLayout.removeContentInactiveRunnable(VISIBLE_TYPE_HEADSUP)
}
if (contentViews and FLAG_CONTENT_VIEW_PUBLIC != 0) {
- row.publicLayout.removeContentInactiveRunnable(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED
- )
+ row.publicLayout.removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED)
}
if (
AsyncHybridViewInflation.isEnabled &&
contentViews and FLAG_CONTENT_VIEW_SINGLE_LINE != 0
) {
- row.privateLayout.removeContentInactiveRunnable(
- NotificationContentView.VISIBLE_TYPE_SINGLELINE
- )
+ row.privateLayout.removeContentInactiveRunnable(VISIBLE_TYPE_SINGLELINE)
}
}
@@ -353,6 +345,8 @@
private val remoteViewCache: NotifRemoteViewCache,
private val entry: NotificationEntry,
private val conversationProcessor: ConversationNotificationProcessor,
+ private val ronExtractor: RichOngoingNotificationContentExtractor,
+ private val ronInflater: RichOngoingNotificationViewInflater,
private val row: ExpandableNotificationRow,
private val isMinimized: Boolean,
private val usesIncreasedHeight: Boolean,
@@ -432,6 +426,7 @@
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
conversationProcessor = conversationProcessor,
+ ronExtractor = ronExtractor,
logger = logger
)
logger.logAsyncTaskProgress(
@@ -463,6 +458,21 @@
)
}
}
+
+ if (reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0) {
+ logger.logAsyncTaskProgress(entry, "inflating RON view")
+ inflationProgress.richOngoingNotificationViewHolder =
+ inflationProgress.contentModel.richOngoingContentModel?.let {
+ ronInflater.inflateView(
+ contentModel = it,
+ existingView = row.privateLayout.contractedChild,
+ entry = entry,
+ systemUiContext = context,
+ parentView = row.privateLayout
+ )
+ }
+ }
+
logger.logAsyncTaskProgress(entry, "getting row image resolver (on wrong thread!)")
val imageResolver = row.imageResolver
// wait for image resolver to finish preloading
@@ -568,6 +578,7 @@
var inflatedSmartReplyState: InflatedSmartReplyState? = null
var expandedInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
var headsUpInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
+ var richOngoingNotificationViewHolder: InflatedContentViewHolder? = null
// Inflated SingleLineView that lacks the UI State
var inflatedSingleLineView: HybridNotificationView? = null
@@ -602,6 +613,7 @@
val inflateHeadsUp =
(reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0 &&
result.remoteViews.headsUp != null)
+
if (inflateContracted || inflateExpanded || inflateHeadsUp) {
logger.logAsyncTaskProgress(entry, "inflating contracted smart reply state")
result.inflatedSmartReplyState = inflater.inflateSmartReplyState(entry)
@@ -643,6 +655,7 @@
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
conversationProcessor: ConversationNotificationProcessor,
+ ronExtractor: RichOngoingNotificationContentExtractor,
logger: NotificationRowContentBinderLogger
): InflationProgress {
// process conversations and extract the messaging style
@@ -651,9 +664,24 @@
conversationProcessor.processNotification(entry, builder, logger)
} else null
+ val richOngoingContentModel =
+ if (reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0) {
+ ronExtractor.extractContentModel(
+ entry = entry,
+ builder = builder,
+ systemUIContext = systemUIContext,
+ packageContext = packageContext
+ )
+ } else {
+ // if we're not re-inflating any RON views, make sure the model doesn't change
+ entry.richOngoingContentModel.value
+ }
+
+ val remoteViewsFlags = getRemoteViewsFlags(reInflateFlags, richOngoingContentModel)
+
val remoteViews =
createRemoteViews(
- reInflateFlags = reInflateFlags,
+ reInflateFlags = remoteViewsFlags,
builder = builder,
isMinimized = isMinimized,
usesIncreasedHeight = usesIncreasedHeight,
@@ -688,6 +716,7 @@
NotificationContentModel(
headsUpStatusBarModel = headsUpStatusBarModel,
singleLineViewModel = singleLineViewModel,
+ richOngoingContentModel = richOngoingContentModel,
)
return InflationProgress(
@@ -815,7 +844,7 @@
val publicLayout = row.publicLayout
val runningInflations = HashMap<Int, CancellationSignal>()
var flag = FLAG_CONTENT_VIEW_CONTRACTED
- if (reInflateFlags and flag != 0) {
+ if (reInflateFlags and flag != 0 && result.remoteViews.contracted != null) {
val isNewView =
!canReapplyRemoteView(
newView = result.remoteViews.contracted,
@@ -829,7 +858,7 @@
}
override val remoteView: RemoteViews
- get() = result.remoteViews.contracted!!
+ get() = result.remoteViews.contracted
}
logger.logAsyncTaskProgress(entry, "applying contracted view")
applyRemoteView(
@@ -847,104 +876,89 @@
callback = callback,
parentLayout = privateLayout,
existingView = privateLayout.contractedChild,
- existingWrapper =
- privateLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED
- ),
+ existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_CONTRACTED),
runningInflations = runningInflations,
applyCallback = applyCallback,
logger = logger
)
}
flag = FLAG_CONTENT_VIEW_EXPANDED
- if (reInflateFlags and flag != 0) {
- if (result.remoteViews.expanded != null) {
- val isNewView =
- !canReapplyRemoteView(
- newView = result.remoteViews.expanded,
- oldView =
- remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
- )
- val applyCallback: ApplyCallback =
- object : ApplyCallback() {
- override fun setResultView(v: View) {
- logger.logAsyncTaskProgress(entry, "expanded view applied")
- result.inflatedExpandedView = v
- }
-
- override val remoteView: RemoteViews
- get() = result.remoteViews.expanded
- }
- logger.logAsyncTaskProgress(entry, "applying expanded view")
- applyRemoteView(
- inflationExecutor = inflationExecutor,
- inflateSynchronously = inflateSynchronously,
- isMinimized = isMinimized,
- result = result,
- reInflateFlags = reInflateFlags,
- inflationId = flag,
- remoteViewCache = remoteViewCache,
- entry = entry,
- row = row,
- isNewView = isNewView,
- remoteViewClickHandler = remoteViewClickHandler,
- callback = callback,
- parentLayout = privateLayout,
- existingView = privateLayout.expandedChild,
- existingWrapper =
- privateLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_EXPANDED
- ),
- runningInflations = runningInflations,
- applyCallback = applyCallback,
- logger = logger
+ if (reInflateFlags and flag != 0 && result.remoteViews.expanded != null) {
+ val isNewView =
+ !canReapplyRemoteView(
+ newView = result.remoteViews.expanded,
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
)
- }
+ val applyCallback: ApplyCallback =
+ object : ApplyCallback() {
+ override fun setResultView(v: View) {
+ logger.logAsyncTaskProgress(entry, "expanded view applied")
+ result.inflatedExpandedView = v
+ }
+
+ override val remoteView: RemoteViews
+ get() = result.remoteViews.expanded
+ }
+ logger.logAsyncTaskProgress(entry, "applying expanded view")
+ applyRemoteView(
+ inflationExecutor = inflationExecutor,
+ inflateSynchronously = inflateSynchronously,
+ isMinimized = isMinimized,
+ result = result,
+ reInflateFlags = reInflateFlags,
+ inflationId = flag,
+ remoteViewCache = remoteViewCache,
+ entry = entry,
+ row = row,
+ isNewView = isNewView,
+ remoteViewClickHandler = remoteViewClickHandler,
+ callback = callback,
+ parentLayout = privateLayout,
+ existingView = privateLayout.expandedChild,
+ existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_EXPANDED),
+ runningInflations = runningInflations,
+ applyCallback = applyCallback,
+ logger = logger
+ )
}
flag = FLAG_CONTENT_VIEW_HEADS_UP
- if (reInflateFlags and flag != 0) {
- if (result.remoteViews.headsUp != null) {
- val isNewView =
- !canReapplyRemoteView(
- newView = result.remoteViews.headsUp,
- oldView =
- remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
- )
- val applyCallback: ApplyCallback =
- object : ApplyCallback() {
- override fun setResultView(v: View) {
- logger.logAsyncTaskProgress(entry, "heads up view applied")
- result.inflatedHeadsUpView = v
- }
-
- override val remoteView: RemoteViews
- get() = result.remoteViews.headsUp
- }
- logger.logAsyncTaskProgress(entry, "applying heads up view")
- applyRemoteView(
- inflationExecutor = inflationExecutor,
- inflateSynchronously = inflateSynchronously,
- isMinimized = isMinimized,
- result = result,
- reInflateFlags = reInflateFlags,
- inflationId = flag,
- remoteViewCache = remoteViewCache,
- entry = entry,
- row = row,
- isNewView = isNewView,
- remoteViewClickHandler = remoteViewClickHandler,
- callback = callback,
- parentLayout = privateLayout,
- existingView = privateLayout.headsUpChild,
- existingWrapper =
- privateLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_HEADSUP
- ),
- runningInflations = runningInflations,
- applyCallback = applyCallback,
- logger = logger
+ if (reInflateFlags and flag != 0 && result.remoteViews.headsUp != null) {
+ val isNewView =
+ !canReapplyRemoteView(
+ newView = result.remoteViews.headsUp,
+ oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
)
- }
+ val applyCallback: ApplyCallback =
+ object : ApplyCallback() {
+ override fun setResultView(v: View) {
+ logger.logAsyncTaskProgress(entry, "heads up view applied")
+ result.inflatedHeadsUpView = v
+ }
+
+ override val remoteView: RemoteViews
+ get() = result.remoteViews.headsUp
+ }
+ logger.logAsyncTaskProgress(entry, "applying heads up view")
+ applyRemoteView(
+ inflationExecutor = inflationExecutor,
+ inflateSynchronously = inflateSynchronously,
+ isMinimized = isMinimized,
+ result = result,
+ reInflateFlags = reInflateFlags,
+ inflationId = flag,
+ remoteViewCache = remoteViewCache,
+ entry = entry,
+ row = row,
+ isNewView = isNewView,
+ remoteViewClickHandler = remoteViewClickHandler,
+ callback = callback,
+ parentLayout = privateLayout,
+ existingView = privateLayout.headsUpChild,
+ existingWrapper = privateLayout.getVisibleWrapper(VISIBLE_TYPE_HEADSUP),
+ runningInflations = runningInflations,
+ applyCallback = applyCallback,
+ logger = logger
+ )
}
flag = FLAG_CONTENT_VIEW_PUBLIC
if (reInflateFlags and flag != 0) {
@@ -979,10 +993,7 @@
callback = callback,
parentLayout = publicLayout,
existingView = publicLayout.contractedChild,
- existingWrapper =
- publicLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_CONTRACTED
- ),
+ existingWrapper = publicLayout.getVisibleWrapper(VISIBLE_TYPE_CONTRACTED),
runningInflations = runningInflations,
applyCallback = applyCallback,
logger = logger
@@ -1359,79 +1370,34 @@
if (runningInflations.isNotEmpty()) {
return false
}
- val privateLayout = row.privateLayout
- val publicLayout = row.publicLayout
logger.logAsyncTaskProgress(entry, "finishing")
- if (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
- if (result.inflatedContentView != null) {
- // New view case
- privateLayout.setContractedChild(result.inflatedContentView)
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_CONTRACTED,
- result.remoteViews.contracted
- )
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
- // Reinflation case. Only update if it's still cached (i.e. view has not been
- // freed while inflating).
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_CONTRACTED,
- result.remoteViews.contracted
- )
- }
+
+ // before updating the content model, stop existing binding if necessary
+ val hasRichOngoingContentModel = result.contentModel.richOngoingContentModel != null
+ val requestedRichOngoing = reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0
+ val rejectedRichOngoing = requestedRichOngoing && !hasRichOngoingContentModel
+ if (result.richOngoingNotificationViewHolder != null || rejectedRichOngoing) {
+ row.privateLayout.mContractedBinderHandle?.dispose()
+ row.privateLayout.mContractedBinderHandle = null
}
- if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
- if (result.inflatedExpandedView != null) {
- privateLayout.setExpandedChild(result.inflatedExpandedView)
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_EXPANDED,
- result.remoteViews.expanded
- )
- } else if (result.remoteViews.expanded == null) {
- privateLayout.setExpandedChild(null)
- remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_EXPANDED,
- result.remoteViews.expanded
- )
- }
- if (result.remoteViews.expanded != null) {
- privateLayout.setExpandedInflatedSmartReplies(
- result.expandedInflatedSmartReplies
- )
- } else {
- privateLayout.setExpandedInflatedSmartReplies(null)
- }
- row.setExpandable(result.remoteViews.expanded != null)
- }
- if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
- if (result.inflatedHeadsUpView != null) {
- privateLayout.setHeadsUpChild(result.inflatedHeadsUpView)
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_HEADS_UP,
- result.remoteViews.headsUp
- )
- } else if (result.remoteViews.headsUp == null) {
- privateLayout.setHeadsUpChild(null)
- remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_HEADS_UP,
- result.remoteViews.headsUp
- )
- }
- if (result.remoteViews.headsUp != null) {
- privateLayout.setHeadsUpInflatedSmartReplies(result.headsUpInflatedSmartReplies)
- } else {
- privateLayout.setHeadsUpInflatedSmartReplies(null)
- }
- }
+
+ // set the content model after disposal and before setting new rich ongoing view
+ entry.setContentModel(result.contentModel)
+ result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
+
+ // set normal remote views (skipping rich ongoing states when that model exists)
+ val remoteViewsFlags =
+ getRemoteViewsFlags(reInflateFlags, result.contentModel.richOngoingContentModel)
+ setContentViewsFromRemoteViews(
+ remoteViewsFlags,
+ entry,
+ remoteViewCache,
+ result,
+ row,
+ isMinimized,
+ )
+
+ // set single line view
if (
AsyncHybridViewInflation.isEnabled &&
reInflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE != 0
@@ -1444,80 +1410,144 @@
} else {
SingleLineViewBinder.bind(viewModel, singleLineView)
}
- privateLayout.setSingleLineView(result.inflatedSingleLineView)
+ row.privateLayout.setSingleLineView(result.inflatedSingleLineView)
}
}
- result.inflatedSmartReplyState?.let { privateLayout.setInflatedSmartReplyState(it) }
- if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) {
- if (result.inflatedPublicView != null) {
- publicLayout.setContractedChild(result.inflatedPublicView)
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_PUBLIC,
- result.remoteViews.public
- )
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
- remoteViewCache.putCachedView(
- entry,
- FLAG_CONTENT_VIEW_PUBLIC,
- result.remoteViews.public
- )
- }
+
+ // after updating the content model, set the view, then start the new binder
+ result.richOngoingNotificationViewHolder?.let { viewHolder ->
+ row.privateLayout.contractedChild = viewHolder.view
+ row.privateLayout.expandedChild = null
+ row.privateLayout.headsUpChild = null
+ row.privateLayout.setExpandedInflatedSmartReplies(null)
+ row.privateLayout.setHeadsUpInflatedSmartReplies(null)
+ row.privateLayout.mContractedBinderHandle =
+ viewHolder.binder.setupContentViewBinder()
+ row.setExpandable(false)
+ remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
+ remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
+ remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
}
- if (AsyncGroupHeaderViewInflation.isEnabled) {
- if (reInflateFlags and FLAG_GROUP_SUMMARY_HEADER != 0) {
- if (result.inflatedGroupHeaderView != null) {
- // We need to set if the row is minimized before setting the group header to
- // make sure the setting of header view works correctly
- row.setIsMinimized(isMinimized)
- row.setGroupHeader(/* headerView= */ result.inflatedGroupHeaderView)
- remoteViewCache.putCachedView(
- entry,
- FLAG_GROUP_SUMMARY_HEADER,
- result.remoteViews.normalGroupHeader
- )
- } else if (remoteViewCache.hasCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)) {
- // Re-inflation case. Only update if it's still cached (i.e. view has not
- // been freed while inflating).
- remoteViewCache.putCachedView(
- entry,
- FLAG_GROUP_SUMMARY_HEADER,
- result.remoteViews.normalGroupHeader
- )
- }
- }
- if (reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) {
- if (result.inflatedMinimizedGroupHeaderView != null) {
- // We need to set if the row is minimized before setting the group header to
- // make sure the setting of header view works correctly
- row.setIsMinimized(isMinimized)
- row.setMinimizedGroupHeader(
- /* headerView= */ result.inflatedMinimizedGroupHeaderView
- )
- remoteViewCache.putCachedView(
- entry,
- FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
- result.remoteViews.minimizedGroupHeader
- )
- } else if (
- remoteViewCache.hasCachedView(entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)
- ) {
- // Re-inflation case. Only update if it's still cached (i.e. view has not
- // been freed while inflating).
- remoteViewCache.putCachedView(
- entry,
- FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
- result.remoteViews.normalGroupHeader
- )
- }
- }
- }
- entry.setContentModel(result.contentModel)
+
Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
endListener?.onAsyncInflationFinished(entry)
return true
}
+ private fun setContentViewsFromRemoteViews(
+ @InflationFlag reInflateFlags: Int,
+ entry: NotificationEntry,
+ remoteViewCache: NotifRemoteViewCache,
+ result: InflationProgress,
+ row: ExpandableNotificationRow,
+ isMinimized: Boolean,
+ ) {
+ val privateLayout = row.privateLayout
+ val publicLayout = row.publicLayout
+ val remoteViewsUpdater = RemoteViewsUpdater(reInflateFlags, entry, remoteViewCache)
+ remoteViewsUpdater.setContentView(
+ FLAG_CONTENT_VIEW_CONTRACTED,
+ result.remoteViews.contracted,
+ result.inflatedContentView,
+ privateLayout::setContractedChild
+ )
+ remoteViewsUpdater.setContentView(
+ FLAG_CONTENT_VIEW_EXPANDED,
+ result.remoteViews.expanded,
+ result.inflatedExpandedView,
+ privateLayout::setExpandedChild
+ )
+ remoteViewsUpdater.setSmartReplies(
+ FLAG_CONTENT_VIEW_EXPANDED,
+ result.remoteViews.expanded,
+ result.expandedInflatedSmartReplies,
+ privateLayout::setExpandedInflatedSmartReplies
+ )
+ if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
+ row.setExpandable(result.remoteViews.expanded != null)
+ }
+ remoteViewsUpdater.setContentView(
+ FLAG_CONTENT_VIEW_HEADS_UP,
+ result.remoteViews.headsUp,
+ result.inflatedHeadsUpView,
+ privateLayout::setHeadsUpChild
+ )
+ remoteViewsUpdater.setSmartReplies(
+ FLAG_CONTENT_VIEW_HEADS_UP,
+ result.remoteViews.headsUp,
+ result.headsUpInflatedSmartReplies,
+ privateLayout::setHeadsUpInflatedSmartReplies
+ )
+ remoteViewsUpdater.setContentView(
+ FLAG_CONTENT_VIEW_PUBLIC,
+ result.remoteViews.public,
+ result.inflatedPublicView,
+ publicLayout::setContractedChild
+ )
+ if (AsyncGroupHeaderViewInflation.isEnabled) {
+ remoteViewsUpdater.setContentView(
+ FLAG_GROUP_SUMMARY_HEADER,
+ result.remoteViews.normalGroupHeader,
+ result.inflatedGroupHeaderView,
+ ) { views ->
+ row.setIsMinimized(isMinimized)
+ row.setGroupHeader(views)
+ }
+ remoteViewsUpdater.setContentView(
+ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+ result.remoteViews.minimizedGroupHeader,
+ result.inflatedMinimizedGroupHeaderView,
+ ) { views ->
+ row.setIsMinimized(isMinimized)
+ row.setMinimizedGroupHeader(views)
+ }
+ }
+ }
+
+ private class RemoteViewsUpdater(
+ @InflationFlag private val reInflateFlags: Int,
+ private val entry: NotificationEntry,
+ private val remoteViewCache: NotifRemoteViewCache,
+ ) {
+ fun <V : View> setContentView(
+ @InflationFlag flagState: Int,
+ remoteViews: RemoteViews?,
+ view: V?,
+ setView: (V?) -> Unit,
+ ) {
+ val clearViewFlags = FLAG_CONTENT_VIEW_HEADS_UP or FLAG_CONTENT_VIEW_EXPANDED
+ val shouldClearView = flagState and clearViewFlags != 0
+ if (reInflateFlags and flagState != 0) {
+ if (view != null) {
+ setView(view)
+ remoteViewCache.putCachedView(entry, flagState, remoteViews)
+ } else if (shouldClearView && remoteViews == null) {
+ setView(null)
+ remoteViewCache.removeCachedView(entry, flagState)
+ } else if (remoteViewCache.hasCachedView(entry, flagState)) {
+ // Re-inflation case. Only update if it's still cached (i.e. view has not
+ // been freed while inflating).
+ remoteViewCache.putCachedView(entry, flagState, remoteViews)
+ }
+ }
+ }
+
+ fun setSmartReplies(
+ @InflationFlag flagState: Int,
+ remoteViews: RemoteViews?,
+ smartReplies: InflatedSmartReplyViewHolder?,
+ setSmartReplies: (InflatedSmartReplyViewHolder?) -> Unit,
+ ) {
+ if (reInflateFlags and flagState != 0) {
+ if (remoteViews != null) {
+ setSmartReplies(smartReplies)
+ } else {
+ setSmartReplies(null)
+ }
+ }
+ }
+ }
+
private fun createExpandedView(
builder: Notification.Builder,
isMinimized: Boolean
@@ -1562,6 +1592,21 @@
!oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED)
}
+ @InflationFlag
+ private fun getRemoteViewsFlags(
+ @InflationFlag reInflateFlags: Int,
+ richOngoingContentModel: RichOngoingContentModel?
+ ): Int =
+ if (richOngoingContentModel != null) {
+ reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING.inv()
+ } else {
+ reInflateFlags
+ }
+
+ @InflationFlag
+ private const val CONTENT_VIEWS_TO_CREATE_RICH_ONGOING =
+ FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
+
private const val ASYNC_TASK_TRACE_METHOD =
"NotificationRowContentBinderImpl.AsyncInflationTask"
private const val APPLY_TRACE_METHOD = "NotificationRowContentBinderImpl#apply"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 84f2f66..c630c4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -18,6 +18,8 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag;
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.RichOngoingViewModelComponent;
import dagger.Binds;
import dagger.Module;
@@ -28,7 +30,7 @@
/**
* Dagger Module containing notification row and view inflation implementations.
*/
-@Module
+@Module(subcomponents = {RichOngoingViewModelComponent.class})
public abstract class NotificationRowModule {
/**
@@ -47,6 +49,25 @@
}
}
+ /** Provides ron content model extractor. */
+ @Provides
+ @SysUISingleton
+ public static RichOngoingNotificationContentExtractor provideRonContentExtractor(
+ Provider<RichOngoingNotificationContentExtractorImpl> realImpl
+ ) {
+ if (RichOngoingNotificationFlag.isEnabled()) {
+ return realImpl.get();
+ } else {
+ return new NoOpRichOngoingNotificationContentExtractor();
+ }
+ }
+
+ /** Provides ron view inflater. */
+ @Binds
+ @SysUISingleton
+ public abstract RichOngoingNotificationViewInflater provideRonViewInflater(
+ RichOngoingNotificationViewInflaterImpl impl);
+
/**
* Provides notification remote view cache instance.
*/
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
new file mode 100644
index 0000000..b5ea861
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
@@ -0,0 +1,170 @@
+/*
+ * 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
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.content.Context
+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.IconModel
+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.TimerContentModel
+import java.time.Duration
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.LocalTime
+import java.time.ZoneId
+import javax.inject.Inject
+
+/**
+ * Interface which provides a [RichOngoingContentModel] for a given [Notification] when one is
+ * applicable to the given style.
+ */
+interface RichOngoingNotificationContentExtractor {
+ fun extractContentModel(
+ entry: NotificationEntry,
+ builder: Notification.Builder,
+ systemUIContext: Context,
+ packageContext: Context
+ ): RichOngoingContentModel?
+}
+
+class NoOpRichOngoingNotificationContentExtractor : RichOngoingNotificationContentExtractor {
+ override fun extractContentModel(
+ entry: NotificationEntry,
+ builder: Notification.Builder,
+ systemUIContext: Context,
+ packageContext: Context
+ ): RichOngoingContentModel? = null
+}
+
+@SysUISingleton
+class RichOngoingNotificationContentExtractorImpl @Inject constructor() :
+ RichOngoingNotificationContentExtractor {
+
+ init {
+ /* check if */ RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()
+ }
+
+ override fun extractContentModel(
+ entry: NotificationEntry,
+ builder: Notification.Builder,
+ systemUIContext: Context,
+ packageContext: Context
+ ): RichOngoingContentModel? =
+ try {
+ val sbn = entry.sbn
+ val notification = sbn.notification
+ val icon = IconModel(notification.smallIcon)
+ if (sbn.packageName == "com.google.android.deskclock") {
+ when (notification.channelId) {
+ "Timers v2" -> {
+ parseTimerNotification(notification, icon)
+ }
+ "Stopwatch v2" -> {
+ Log.i("RONs", "Can't process stopwatch yet")
+ null
+ }
+ else -> {
+ Log.i("RONs", "Can't process channel '${notification.channelId}'")
+ null
+ }
+ }
+ } else null
+ } catch (e: Exception) {
+ Log.e("RONs", "Error parsing RON", e)
+ null
+ }
+
+ /**
+ * FOR PROTOTYPING ONLY: create a RON TimerContentModel using the time information available
+ * inside the sortKey of the clock app's timer notifications.
+ */
+ private fun parseTimerNotification(
+ notification: Notification,
+ icon: IconModel
+ ): TimerContentModel {
+ // sortKey=1 0|↺7|RUNNING|▶16:21:58.523|Σ0:05:00|Δ0:00:03|⏳0:04:57
+ // sortKey=1 0|↺7|PAUSED|Σ0:05:00|Δ0:04:54|⏳0:00:06
+ // sortKey=1 1|↺7|RUNNING|▶16:30:28.433|Σ0:04:05|Δ0:00:06|⏳0:03:59
+ // sortKey=1 0|↺7|RUNNING|▶16:36:18.350|Σ0:05:00|Δ0:01:42|⏳0:03:18
+ // sortKey=1 2|↺7|RUNNING|▶16:38:37.816|Σ0:02:00|Δ0:01:09|⏳0:00:51
+ // ▶ = "current" time (when updated)
+ // Σ = total time
+ // Δ = time elapsed
+ // ⏳ = time remaining
+ val sortKey = notification.sortKey
+ val (_, _, state, extra) = sortKey.split("|", limit = 4)
+ return when (state) {
+ "PAUSED" -> {
+ val (total, _, remaining) = extra.split("|")
+ val timeRemaining = parseTimeDelta(remaining)
+ TimerContentModel(
+ icon = icon,
+ name = total,
+ state =
+ TimerContentModel.TimerState.Paused(
+ timeRemaining = timeRemaining,
+ resumeIntent = notification.findActionWithName("Resume"),
+ resetIntent = notification.findActionWithName("Reset"),
+ )
+ )
+ }
+ "RUNNING" -> {
+ val (current, total, _, remaining) = extra.split("|")
+ val finishTime = parseCurrentTime(current) + parseTimeDelta(remaining).toMillis()
+ TimerContentModel(
+ icon = icon,
+ name = total,
+ state =
+ TimerContentModel.TimerState.Running(
+ finishTime = finishTime,
+ pauseIntent = notification.findActionWithName("Pause"),
+ addOneMinuteIntent = notification.findActionWithName("Add 1 min"),
+ )
+ )
+ }
+ else -> error("unknown state ($state) in sortKey=$sortKey")
+ }
+ }
+
+ private fun Notification.findActionWithName(name: String): PendingIntent? {
+ return actions.firstOrNull { name == it.title?.toString() }?.actionIntent
+ }
+
+ private fun parseCurrentTime(current: String): Long {
+ val (hour, minute, second, millis) = current.replace("▶", "").split(":", ".")
+ // NOTE: this won't work correctly at/around midnight. It's just for prototyping.
+ val localDateTime =
+ LocalDateTime.of(
+ LocalDate.now(),
+ LocalTime.of(hour.toInt(), minute.toInt(), second.toInt(), millis.toInt() * 1000000)
+ )
+ val offset = ZoneId.systemDefault().rules.getOffset(localDateTime)
+ return localDateTime.toInstant(offset).toEpochMilli()
+ }
+
+ private fun parseTimeDelta(delta: String): Duration {
+ val (hour, minute, second) = delta.replace("Σ", "").replace("⏳", "").split(":")
+ return Duration.ofHours(hour.toLong())
+ .plusMinutes(minute.toLong())
+ .plusSeconds(second.toLong())
+ }
+}
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
new file mode 100644
index 0000000..e9c4960
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
@@ -0,0 +1,104 @@
+/*
+ * 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
+
+import android.app.Notification
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+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.TimerView
+import com.android.systemui.statusbar.notification.row.ui.viewbinder.TimerViewBinder
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.RichOngoingViewModelComponent
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.TimerViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+fun interface DeferredContentViewBinder {
+ fun setupContentViewBinder(): DisposableHandle
+}
+
+class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder)
+
+/**
+ * Interface which provides a [RichOngoingContentModel] for a given [Notification] when one is
+ * applicable to the given style.
+ */
+interface RichOngoingNotificationViewInflater {
+ fun inflateView(
+ contentModel: RichOngoingContentModel,
+ existingView: View?,
+ entry: NotificationEntry,
+ systemUiContext: Context,
+ parentView: ViewGroup,
+ ): InflatedContentViewHolder?
+}
+
+@SysUISingleton
+class RichOngoingNotificationViewInflaterImpl
+@Inject
+constructor(
+ private val viewModelComponentFactory: RichOngoingViewModelComponent.Factory,
+) : RichOngoingNotificationViewInflater {
+
+ override fun inflateView(
+ contentModel: RichOngoingContentModel,
+ existingView: View?,
+ entry: NotificationEntry,
+ systemUiContext: Context,
+ parentView: ViewGroup,
+ ): InflatedContentViewHolder? {
+ if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return null
+ val component = viewModelComponentFactory.create(entry)
+ return when (contentModel) {
+ is TimerContentModel ->
+ inflateTimerView(
+ existingView,
+ component::createTimerViewModel,
+ systemUiContext,
+ parentView
+ )
+ is StopwatchContentModel -> TODO("Not yet implemented")
+ }
+ }
+
+ private fun inflateTimerView(
+ existingView: View?,
+ createViewModel: () -> TimerViewModel,
+ systemUiContext: Context,
+ parentView: ViewGroup,
+ ): InflatedContentViewHolder? {
+ if (existingView is TimerView && !existingView.isReinflateNeeded()) return null
+ val newView =
+ LayoutInflater.from(systemUiContext)
+ .inflate(
+ R.layout.rich_ongoing_timer_notification,
+ parentView,
+ /* attachToRoot= */ false
+ ) as TimerView
+ return InflatedContentViewHolder(newView) {
+ TimerViewBinder.bindWhileAttached(newView, createViewModel())
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepository.kt
new file mode 100644
index 0000000..bac887b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepository.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.data.repository
+
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
+import kotlinx.coroutines.flow.StateFlow
+
+/** A repository of states relating to a specific notification row. */
+interface NotificationRowRepository {
+ /**
+ * A flow of an immutable data class with the current state of the Rich Ongoing Notification
+ * content, if applicable.
+ */
+ val richOngoingContentModel: StateFlow<RichOngoingContentModel?>
+}
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
new file mode 100644
index 0000000..4705ace
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.row.domain.interactor
+
+import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
+import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterIsInstance
+
+/** Interactor specific to a particular notification row. */
+class NotificationRowInteractor @Inject constructor(repository: NotificationRowRepository) {
+ /** Content of a rich ongoing timer notification. */
+ val timerContentModel: Flow<TimerContentModel> =
+ repository.richOngoingContentModel.filterIsInstance<TimerContentModel>()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/IconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/IconModel.kt
new file mode 100644
index 0000000..e611938
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/IconModel.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.shared
+
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+
+// TODO: figure out how to support lazy resolution of the drawable, e.g. on unrelated text change
+class IconModel(val icon: Icon) {
+ var drawable: Drawable? = null
+
+ override fun equals(other: Any?): Boolean =
+ when (other) {
+ null -> false
+ (other === this) -> true
+ !is IconModel -> false
+ else -> other.icon.sameAs(icon)
+ }
+
+ override fun toString(): String = "IconModel(icon=$icon, drawable=$drawable)"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
index b2421bc..46010a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
@@ -21,4 +21,7 @@
data class NotificationContentModel(
val headsUpStatusBarModel: HeadsUpStatusBarModel,
val singleLineViewModel: SingleLineViewModel? = null,
+ val richOngoingContentModel: RichOngoingContentModel? = null,
)
+
+sealed interface RichOngoingContentModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingClock.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingClock.kt
new file mode 100644
index 0000000..5584701
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingClock.kt
@@ -0,0 +1,97 @@
+/*
+ * 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
+
+import android.app.PendingIntent
+import java.time.Duration
+
+/**
+ * Represents a simple timer that counts down to a time.
+ *
+ * @param name the label for the timer
+ * @param state state of the timer, including time and whether it is paused or running
+ */
+data class TimerContentModel(
+ val icon: IconModel,
+ val name: String,
+ val state: TimerState,
+) : RichOngoingContentModel {
+ /** The state (paused or running) of the timer, and relevant time */
+ sealed interface TimerState {
+ /**
+ * Indicates a running timer
+ *
+ * @param finishTime the time in ms since epoch that the timer will finish
+ * @param pauseIntent the action for pausing the timer
+ */
+ data class Running(
+ val finishTime: Long,
+ val pauseIntent: PendingIntent?,
+ val addOneMinuteIntent: PendingIntent?,
+ ) : TimerState
+
+ /**
+ * Indicates a paused timer
+ *
+ * @param timeRemaining the time in ms remaining on the paused timer
+ * @param resumeIntent the action for resuming the timer
+ */
+ data class Paused(
+ val timeRemaining: Duration,
+ val resumeIntent: PendingIntent?,
+ val resetIntent: PendingIntent?,
+ ) : TimerState
+ }
+}
+
+/**
+ * Represents a simple stopwatch that counts up and allows tracking laps.
+ *
+ * @param state state of the stopwatch, including time and whether it is paused or running
+ * @param lapDurations a list of durations of each completed lap
+ */
+data class StopwatchContentModel(
+ val icon: IconModel,
+ val state: StopwatchState,
+ val lapDurations: List<Long>,
+) : RichOngoingContentModel {
+ /** The state (paused or running) of the stopwatch, and relevant time */
+ sealed interface StopwatchState {
+ /**
+ * Indicates a running stopwatch
+ *
+ * @param startTime the time in ms since epoch that the stopwatch started, plus any
+ * accumulated pause time
+ * @param pauseIntent the action for pausing the stopwatch
+ */
+ data class Running(
+ val startTime: Long,
+ val pauseIntent: PendingIntent,
+ ) : StopwatchState
+
+ /**
+ * Indicates a paused stopwatch
+ *
+ * @param timeElapsed the time in ms elapsed on the stopwatch
+ * @param resumeIntent the action for resuming the stopwatch
+ */
+ data class Paused(
+ val timeElapsed: Duration,
+ val resumeIntent: PendingIntent,
+ ) : StopwatchState
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingNotificationFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingNotificationFlag.kt
new file mode 100644
index 0000000..4a7f7cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/RichOngoingNotificationFlag.kt
@@ -0,0 +1,53 @@
+/*
+ * 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
+
+import android.app.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the api rich ongoing flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object RichOngoingNotificationFlag {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_API_RICH_ONGOING
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.apiRichOngoing()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/ConfigurationTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/ConfigurationTracker.kt
new file mode 100644
index 0000000..95c507c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/ConfigurationTracker.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.pm.ActivityInfo.CONFIG_ASSETS_PATHS
+import android.content.pm.ActivityInfo.CONFIG_DENSITY
+import android.content.pm.ActivityInfo.CONFIG_FONT_SCALE
+import android.content.pm.ActivityInfo.CONFIG_LAYOUT_DIRECTION
+import android.content.pm.ActivityInfo.CONFIG_LOCALE
+import android.content.pm.ActivityInfo.CONFIG_UI_MODE
+import android.content.res.Configuration
+import android.content.res.Resources
+
+/**
+ * Tracks the active configuration when constructed and returns (when queried) whether the
+ * configuration has unhandled changes.
+ */
+class ConfigurationTracker(
+ private val resources: Resources,
+ private val unhandledConfigChanges: Int
+) {
+ private val initialConfig = Configuration(resources.configuration)
+
+ constructor(
+ resources: Resources,
+ handlesDensityFontScale: Boolean = false,
+ handlesTheme: Boolean = false,
+ handlesLocaleAndLayout: Boolean = true,
+ ) : this(
+ resources,
+ unhandledConfigChanges =
+ (if (handlesDensityFontScale) 0 else CONFIG_DENSITY or CONFIG_FONT_SCALE) or
+ (if (handlesTheme) 0 else CONFIG_ASSETS_PATHS or CONFIG_UI_MODE) or
+ (if (handlesLocaleAndLayout) 0 else CONFIG_LOCALE or CONFIG_LAYOUT_DIRECTION)
+ )
+
+ /**
+ * Whether the current configuration has unhandled changes relative to the initial configuration
+ */
+ fun hasUnhandledConfigChange(): Boolean =
+ initialConfig.diff(resources.configuration) and unhandledConfigChanges != 0
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerButtonView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerButtonView.kt
new file mode 100644
index 0000000..0d83ace
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerButtonView.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.view
+
+import android.annotation.DrawableRes
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.Button
+
+class TimerButtonView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+) : Button(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val Int.dp: Int
+ get() = (this * context.resources.displayMetrics.density).toInt()
+
+ fun setIcon(@DrawableRes icon: Int) {
+ val drawable = context.getDrawable(icon)
+ drawable?.setBounds(0, 0, 24.dp, 24.dp)
+ setCompoundDrawablesRelative(drawable, null, null, null)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerView.kt
new file mode 100644
index 0000000..2e164d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/TimerView.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.Drawable
+import android.os.SystemClock
+import android.util.AttributeSet
+import android.widget.Chronometer
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isVisible
+import com.android.systemui.res.R
+
+class TimerView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val configTracker = ConfigurationTracker(resources)
+
+ private lateinit var icon: ImageView
+ private lateinit var label: TextView
+ private lateinit var chronometer: Chronometer
+ private lateinit var pausedTimeRemaining: TextView
+ lateinit var mainButton: TimerButtonView
+ private set
+
+ lateinit var altButton: TimerButtonView
+ private set
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ icon = requireViewById(R.id.icon)
+ label = requireViewById(R.id.label)
+ chronometer = requireViewById(R.id.chronoRemaining)
+ pausedTimeRemaining = requireViewById(R.id.pausedTimeRemaining)
+ mainButton = requireViewById(R.id.mainButton)
+ altButton = requireViewById(R.id.altButton)
+ }
+
+ /** the resources configuration has changed such that the view needs to be reinflated */
+ fun isReinflateNeeded(): Boolean = configTracker.hasUnhandledConfigChange()
+
+ fun setIcon(iconDrawable: Drawable?) {
+ this.icon.setImageDrawable(iconDrawable)
+ }
+
+ fun setLabel(label: String) {
+ this.label.text = label
+ }
+
+ fun setPausedTime(pausedTime: String?) {
+ if (pausedTime != null) {
+ pausedTimeRemaining.text = pausedTime
+ pausedTimeRemaining.isVisible = true
+ } else {
+ pausedTimeRemaining.isVisible = false
+ }
+ }
+
+ fun setCountdownTime(countdownTimeMs: Long?) {
+ if (countdownTimeMs != null) {
+ chronometer.base =
+ countdownTimeMs - System.currentTimeMillis() + SystemClock.elapsedRealtime()
+ chronometer.isVisible = true
+ chronometer.start()
+ } else {
+ chronometer.isVisible = false
+ chronometer.stop()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/TimerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/TimerViewBinder.kt
new file mode 100644
index 0000000..c9ff589
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/TimerViewBinder.kt
@@ -0,0 +1,64 @@
+/*
+ * 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 android.view.View
+import androidx.core.view.isGone
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
+import com.android.systemui.statusbar.notification.row.ui.view.TimerView
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.TimerViewModel
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/** Binds a [TimerView] to its [view model][TimerViewModel]. */
+object TimerViewBinder {
+ fun bindWhileAttached(
+ view: TimerView,
+ viewModel: TimerViewModel,
+ ): DisposableHandle {
+ return view.repeatWhenAttached { lifecycleScope.launch { bind(view, viewModel) } }
+ }
+
+ suspend fun bind(
+ view: TimerView,
+ viewModel: TimerViewModel,
+ ) = coroutineScope {
+ launch { viewModel.icon.collect { view.setIcon(it) } }
+ launch { viewModel.label.collect { view.setLabel(it) } }
+ launch { viewModel.pausedTime.collect { view.setPausedTime(it) } }
+ launch { viewModel.countdownTime.collect { view.setCountdownTime(it) } }
+ launch { viewModel.mainButtonModel.collect { bind(view.mainButton, it) } }
+ launch { viewModel.altButtonModel.collect { bind(view.altButton, it) } }
+ }
+
+ fun bind(buttonView: TimerButtonView, model: TimerViewModel.ButtonViewModel?) {
+ if (model != null) {
+ buttonView.setIcon(model.iconRes)
+ buttonView.setText(model.labelRes)
+ buttonView.setOnClickListener(
+ model.pendingIntent?.let { pendingIntent ->
+ View.OnClickListener { pendingIntent.send() }
+ }
+ )
+ buttonView.isEnabled = model.pendingIntent != null
+ }
+ buttonView.isGone = model == null
+ }
+}
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
new file mode 100644
index 0000000..dad52a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
@@ -0,0 +1,36 @@
+/*
+ * 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
+
+// noinspection CleanArchitectureDependencyViolation
+import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+@Subcomponent
+interface RichOngoingViewModelComponent {
+
+ @Subcomponent.Factory
+ interface Factory {
+ /** Creates an instance of [RichOngoingViewModelComponent]. */
+ fun create(
+ @BindsInstance repository: NotificationRowRepository
+ ): RichOngoingViewModelComponent
+ }
+
+ fun createTimerViewModel(): TimerViewModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModel.kt
new file mode 100644
index 0000000..a85c87f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModel.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.annotation.DrawableRes
+import android.annotation.StringRes
+import android.app.PendingIntent
+import android.graphics.drawable.Drawable
+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.statusbar.notification.row.shared.TimerContentModel.TimerState
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import java.time.Duration
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+
+/** A view model for Timer notifications. */
+class TimerViewModel
+@Inject
+constructor(
+ dumpManager: DumpManager,
+ rowInteractor: NotificationRowInteractor,
+) : FlowDumperImpl(dumpManager) {
+ init {
+ /* check if */ RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()
+ }
+
+ private val state: Flow<TimerState> = rowInteractor.timerContentModel.mapNotNull { it.state }
+
+ val icon: Flow<Drawable?> = rowInteractor.timerContentModel.mapNotNull { it.icon.drawable }
+
+ val label: Flow<String> = rowInteractor.timerContentModel.mapNotNull { it.name }
+
+ val countdownTime: Flow<Long?> = state.map { (it as? TimerState.Running)?.finishTime }
+
+ val pausedTime: Flow<String?> =
+ state.map { (it as? TimerState.Paused)?.timeRemaining?.format() }
+
+ val mainButtonModel: Flow<ButtonViewModel> =
+ state.map {
+ when (it) {
+ is TimerState.Paused ->
+ ButtonViewModel(
+ it.resumeIntent,
+ com.android.systemui.res.R.string.controls_media_resume, // "Resume",
+ com.android.systemui.res.R.drawable.ic_media_play
+ )
+ is TimerState.Running ->
+ ButtonViewModel(
+ it.pauseIntent,
+ com.android.systemui.res.R.string.controls_media_button_pause, // "Pause",
+ com.android.systemui.res.R.drawable.ic_media_pause
+ )
+ }
+ }
+
+ val altButtonModel: Flow<ButtonViewModel?> =
+ state.map {
+ when (it) {
+ is TimerState.Paused ->
+ it.resetIntent?.let { resetIntent ->
+ ButtonViewModel(
+ resetIntent,
+ com.android.systemui.res.R.string.reset, // "Reset",
+ com.android.systemui.res.R.drawable.ic_close_white_rounded
+ )
+ }
+ is TimerState.Running ->
+ it.addOneMinuteIntent?.let { addOneMinuteIntent ->
+ ButtonViewModel(
+ addOneMinuteIntent,
+ com.android.systemui.res.R.string.add, // "Add 1 minute",
+ com.android.systemui.res.R.drawable.ic_add
+ )
+ }
+ }
+ }
+
+ data class ButtonViewModel(
+ val pendingIntent: PendingIntent?,
+ @StringRes val labelRes: Int,
+ @DrawableRes val iconRes: Int,
+ )
+}
+
+private fun Duration.format(): String {
+ val hours = this.toHours()
+ return if (hours > 0) {
+ String.format("%d:%02d:%02d", hours, toMinutesPart(), toSecondsPart())
+ } else {
+ String.format("%d:%02d", toMinutes(), toSecondsPart())
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 9394249..f352123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -36,6 +36,7 @@
import com.android.app.animation.Interpolators;
import com.android.internal.widget.CachingIconView;
+import com.android.internal.widget.NotificationCloseButton;
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.TransformableView;
@@ -60,6 +61,7 @@
= new PathInterpolator(0.4f, 0f, 0.7f, 1f);
protected final ViewTransformationHelper mTransformationHelper;
private CachingIconView mIcon;
+ private NotificationCloseButton mCloseButton;
private NotificationExpandButton mExpandButton;
private View mAltExpandTarget;
private View mIconContainer;
@@ -112,6 +114,7 @@
TRANSFORMING_VIEW_TITLE);
resolveHeaderViews();
addFeedbackOnClickListener(row);
+ addCloseButtonOnClickListener(row);
}
@Override
@@ -150,6 +153,7 @@
mNotificationTopLine = mView.findViewById(com.android.internal.R.id.notification_top_line);
mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon);
mFeedbackIcon = mView.findViewById(com.android.internal.R.id.feedback);
+ mCloseButton = mView.findViewById(com.android.internal.R.id.close_button);
}
private void addFeedbackOnClickListener(ExpandableNotificationRow row) {
@@ -179,6 +183,13 @@
}
}
+ private void addCloseButtonOnClickListener(ExpandableNotificationRow row) {
+ View.OnClickListener listener = row.getCloseButtonOnClickListener(row);
+ if (mCloseButton != null && listener != null) {
+ mCloseButton.setOnClickListener(listener);
+ }
+ }
+
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 5f4e832..fbddc06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -29,6 +29,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -63,6 +64,8 @@
* Used to read bouncer states.
*/
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private float mStackTop;
+ private float mStackCutoff;
private int mScrollY;
private float mOverScrollTopAmount;
private float mOverScrollBottomAmount;
@@ -128,13 +131,13 @@
/** Distance of top of notifications panel from top of screen. */
private float mStackY = 0;
- /** Height of notifications panel. */
+ /** Height of notifications panel interpolated by the expansion fraction. */
private float mStackHeight = 0;
/** Fraction of shade expansion. */
private float mExpansionFraction;
- /** Height of the notifications panel without top padding when expansion completes. */
+ /** Height of the notifications panel when expansion completes. */
private float mStackEndHeight;
/** Whether we are swiping up. */
@@ -173,8 +176,7 @@
}
/**
- * @param stackEndHeight Height of the notifications panel without top padding
- * when expansion completes.
+ * @see #getStackEndHeight()
*/
public void setStackEndHeight(float stackEndHeight) {
mStackEndHeight = stackEndHeight;
@@ -184,6 +186,7 @@
* @param stackY Distance of top of notifications panel from top of screen.
*/
public void setStackY(float stackY) {
+ SceneContainerFlag.assertInLegacyMode();
mStackY = stackY;
}
@@ -191,6 +194,7 @@
* @return Distance of top of notifications panel from top of screen.
*/
public float getStackY() {
+ SceneContainerFlag.assertInLegacyMode();
return mStackY;
}
@@ -252,14 +256,14 @@
}
/**
- * @param stackHeight Height of notifications panel.
+ * @see #getStackHeight()
*/
public void setStackHeight(float stackHeight) {
mStackHeight = stackHeight;
}
/**
- * @return Height of notifications panel.
+ * @return Height of notifications panel interpolated by the expansion fraction.
*/
public float getStackHeight() {
return mStackHeight;
@@ -346,6 +350,33 @@
return mZDistanceBetweenElements;
}
+ /** Y coordinate in view pixels of the top of the notification stack */
+ public float getStackTop() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+ return mStackTop;
+ }
+
+ /** @see #getStackTop() */
+ public void setStackTop(float mStackTop) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ this.mStackTop = mStackTop;
+ }
+
+ /**
+ * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
+ * must be.
+ */
+ public float getStackCutoff() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+ return mStackCutoff;
+ }
+
+ /** @see #getStackCutoff() */
+ public void setStackCutoff(float stackCutoff) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ this.mStackCutoff = stackCutoff;
+ }
+
public int getScrollY() {
return mScrollY;
}
@@ -752,6 +783,8 @@
@Override
public void dump(PrintWriter pw, String[] args) {
+ pw.println("mStackTop=" + mStackTop);
+ pw.println("mStackCutoff" + mStackCutoff);
pw.println("mTopPadding=" + mTopPadding);
pw.println("mStackTopMargin=" + mStackTopMargin);
pw.println("mStackTranslation=" + mStackTranslation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 60a26dc..715c6e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -89,7 +89,6 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.TouchLogger;
@@ -620,8 +619,6 @@
@Nullable
private OnClickListener mManageButtonClickListener;
- @Nullable
- private OnNotificationRemovedListener mOnNotificationRemovedListener;
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0, 0);
@@ -773,10 +770,6 @@
&& !mIsRemoteInputActive;
}
- public NotificationSwipeActionHelper getSwipeActionHelper() {
- return mSwipeHelper;
- }
-
void updateBgColor() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
@@ -834,10 +827,10 @@
drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y);
if (SceneContainerFlag.isEnabled()) {
- y = (int) mScrollViewFields.getStackTop();
+ y = (int) mAmbientState.getStackTop();
drawDebugInfo(canvas, y, Color.RED, /* label= */ "getStackTop() = " + y);
- y = (int) mScrollViewFields.getStackCutoff();
+ y = (int) mAmbientState.getStackCutoff();
drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "getStackCutoff() = " + y);
y = (int) mScrollViewFields.getHeadsUpTop();
@@ -1181,9 +1174,11 @@
updateAlgorithmLayoutMinHeight();
updateOwnTranslationZ();
- // Give The Algorithm information regarding the QS height so it can layout notifications
- // properly. Needed for some devices that grows notifications down-to-top
- mStackScrollAlgorithm.updateQSFrameTop(mQsHeader == null ? 0 : mQsHeader.getHeight());
+ if (!SceneContainerFlag.isEnabled()) {
+ // Give The Algorithm information regarding the QS height so it can layout notifications
+ // properly. Needed for some devices that grows notifications down-to-top
+ mStackScrollAlgorithm.updateQSFrameTop(mQsHeader == null ? 0 : mQsHeader.getHeight());
+ }
// Once the layout has finished, we don't need to animate any scrolling clampings anymore.
mAnimateStackYForContentHeightChange = false;
@@ -1214,14 +1209,14 @@
@Override
public void setStackTop(float stackTop) {
- mScrollViewFields.setStackTop(stackTop);
+ mAmbientState.setStackTop(stackTop);
// TODO(b/332574413): replace the following with using stackTop
updateTopPadding(stackTop, isAddOrRemoveAnimationPending());
}
@Override
public void setStackCutoff(float stackCutoff) {
- mScrollViewFields.setStackCutoff(stackCutoff);
+ mAmbientState.setStackCutoff(stackCutoff);
}
@Override
@@ -1424,11 +1419,7 @@
if (mAmbientState.isBouncerInTransit() && mQsExpansionFraction > 0f) {
fraction = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(fraction);
}
- // TODO(b/322228881): Clean up scene container vs legacy behavior in NSSL
- if (SceneContainerFlag.isEnabled()) {
- // stackY should be driven by scene container, not NSSL
- mAmbientState.setStackY(getTopPadding());
- } else {
+ if (!SceneContainerFlag.isEnabled()) {
final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY);
}
@@ -1442,22 +1433,40 @@
@VisibleForTesting
public void updateStackEndHeightAndStackHeight(float fraction) {
final float oldStackHeight = mAmbientState.getStackHeight();
- if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
- final float endHeight = updateStackEndHeight(
- getHeight(), getEmptyBottomMargin(), getTopPadding());
+ if (SceneContainerFlag.isEnabled()) {
+ final float endHeight;
+ if (!shouldSkipHeightUpdate()) {
+ endHeight = updateStackEndHeight();
+ } else {
+ endHeight = mAmbientState.getStackEndHeight();
+ }
updateStackHeight(endHeight, fraction);
} else {
- // Always updateStackHeight to prevent jumps in the stack height when this fraction
- // suddenly reapplies after a freeze.
- final float endHeight = mAmbientState.getStackEndHeight();
- updateStackHeight(endHeight, fraction);
+ if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
+ final float endHeight = updateStackEndHeight(
+ getHeight(), getEmptyBottomMargin(), getTopPadding());
+ updateStackHeight(endHeight, fraction);
+ } else {
+ // Always updateStackHeight to prevent jumps in the stack height when this fraction
+ // suddenly reapplies after a freeze.
+ final float endHeight = mAmbientState.getStackEndHeight();
+ updateStackHeight(endHeight, fraction);
+ }
}
if (oldStackHeight != mAmbientState.getStackHeight()) {
requestChildrenUpdate();
}
}
+ private float updateStackEndHeight() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+ float height = Math.max(0f, mAmbientState.getStackCutoff() - mAmbientState.getStackTop());
+ mAmbientState.setStackEndHeight(height);
+ return height;
+ }
+
private float updateStackEndHeight(float height, float bottomMargin, float topPadding) {
+ SceneContainerFlag.assertInLegacyMode();
final float stackEndHeight;
if (mMaxDisplayedNotifications != -1) {
// The stack intrinsic height already contains the correct value when there is a limit
@@ -1491,6 +1500,7 @@
* needed.
*/
void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
+ SceneContainerFlag.assertInLegacyMode();
mOnStackYChanged = onStackYChanged;
}
@@ -1811,6 +1821,7 @@
}
public void setQsHeader(ViewGroup qsHeader) {
+ SceneContainerFlag.assertInLegacyMode();
mQsHeader = qsHeader;
}
@@ -1963,17 +1974,6 @@
return insets;
}
- private final Runnable mReclamp = new Runnable() {
- @Override
- public void run() {
- int range = getScrollRange();
- mScroller.startScroll(mScrollX, mOwnScrollY, 0, range - mOwnScrollY);
- mDontReportNextOverScroll = true;
- mDontClampNextScroll = true;
- animateScroll();
- }
- };
-
public void setExpandingEnabled(boolean enable) {
mExpandHelper.setEnabled(enable);
}
@@ -2271,6 +2271,7 @@
public void setOverscrollTopChangedListener(
OnOverscrollTopChangedListener overscrollTopChangedListener) {
+ SceneContainerFlag.assertInLegacyMode();
mOverscrollTopChangedListener = overscrollTopChangedListener;
}
@@ -2427,17 +2428,6 @@
return null;
}
- private ExpandableNotificationRow getLastRowNotGone() {
- int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
- return (ExpandableNotificationRow) child;
- }
- }
- return null;
- }
-
/**
* @return the number of children which have visibility unequal to GONE
*/
@@ -2662,6 +2652,7 @@
}
public void setMaxTopPadding(int maxTopPadding) {
+ SceneContainerFlag.assertInLegacyMode();
mMaxTopPadding = maxTopPadding;
}
@@ -2682,13 +2673,10 @@
}
public float getTopPaddingOverflow() {
+ SceneContainerFlag.assertInLegacyMode();
return mTopPaddingOverflow;
}
- private int clampPadding(int desiredPadding) {
- return Math.max(desiredPadding, mIntrinsicPadding);
- }
-
private float getRubberBandFactor(boolean onTop) {
if (!onTop) {
return RUBBER_BAND_FACTOR_NORMAL;
@@ -3721,7 +3709,7 @@
protected boolean isInsideQsHeader(MotionEvent ev) {
if (SceneContainerFlag.isEnabled()) {
- return ev.getY() < mScrollViewFields.getStackTop();
+ return ev.getY() < mAmbientState.getStackTop();
}
mQsHeader.getBoundsOnScreen(mQsHeaderBound);
@@ -4455,6 +4443,7 @@
}
void goToFullShade(long delay) {
+ SceneContainerFlag.assertInLegacyMode();
mGoToFullShadeNeedsAnimation = true;
mGoToFullShadeDelay = delay;
mNeedsAnimation = true;
@@ -4641,6 +4630,7 @@
}
public boolean isEmptyShadeViewVisible() {
+ SceneContainerFlag.assertInLegacyMode();
return mEmptyShadeView.isVisible();
}
@@ -4919,6 +4909,7 @@
}
public void setQsFullScreen(boolean qsFullScreen) {
+ SceneContainerFlag.assertInLegacyMode();
if (FooterViewRefactor.isEnabled()) {
if (qsFullScreen == mQsFullScreen) {
return; // no change
@@ -5095,6 +5086,7 @@
}
public void setExpandingVelocity(float expandingVelocity) {
+ SceneContainerFlag.assertInLegacyMode();
mAmbientState.setExpandingVelocity(expandingVelocity);
}
@@ -5715,6 +5707,7 @@
* Set a listener to when scrolling changes.
*/
public void setOnScrollListener(Consumer<Integer> listener) {
+ SceneContainerFlag.assertInLegacyMode();
mScrollListener = listener;
}
@@ -6560,16 +6553,4 @@
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
}
-
- /**
- *
- */
- public interface OnNotificationRemovedListener {
- /**
- *
- * @param child
- * @param isTransferInProgress
- */
- void onNotificationRemoved(ExpandableView child, boolean isTransferInProgress);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d984685..12f8f69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1052,6 +1052,7 @@
public void setOverscrollTopChangedListener(
OnOverscrollTopChangedListener listener) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setOverscrollTopChangedListener(listener);
}
@@ -1177,6 +1178,7 @@
}
public void goToFullShade(long delay) {
+ SceneContainerFlag.assertInLegacyMode();
mView.goToFullShade(delay);
}
@@ -1247,6 +1249,7 @@
}
public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setOnStackYChanged(onStackYChanged);
}
@@ -1749,6 +1752,7 @@
* Set a listener to when scrolling changes.
*/
public void setOnScrollListener(Consumer<Integer> listener) {
+ SceneContainerFlag.assertInLegacyMode();
mView.setOnScrollListener(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index bffc170..97ec391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -32,13 +32,6 @@
class ScrollViewFields {
/** Used to produce the clipping path */
var scrimClippingShape: ShadeScrimShape? = null
- /** Y coordinate in view pixels of the top of the notification stack */
- var stackTop: Float = 0f
- /**
- * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
- * must be.
- */
- var stackCutoff: Float = 0f
/** Y coordinate in view pixels of the top of the HUN */
var headsUpTop: Float = 0f
/** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */
@@ -81,8 +74,6 @@
fun dump(pw: IndentingPrintWriter) {
pw.printSection("StackViewStates") {
pw.println("scrimClippingShape", scrimClippingShape)
- pw.println("stackTop", stackTop)
- pw.println("stackCutoff", stackCutoff)
pw.println("headsUpTop", headsUpTop)
pw.println("isScrolledToTop", isScrolledToTop)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index f9efc07..ee7b5c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -29,6 +29,7 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
@@ -332,8 +333,10 @@
private void updateClipping(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
- float drawStart = ambientState.isOnKeyguard() ? 0
+ float stackTop = SceneContainerFlag.isEnabled() ? ambientState.getStackTop()
: ambientState.getStackY() - ambientState.getScrollY();
+ float drawStart = ambientState.isOnKeyguard() ? 0
+ : stackTop;
float clipStart = 0;
int childCount = algorithmState.visibleChildren.size();
boolean firstHeadsUp = true;
@@ -442,12 +445,26 @@
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
int notGoneIndex = 0;
+ boolean emptyShadeVisible = false;
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) mHostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
if (v == ambientState.getShelf()) {
continue;
}
+ if (FooterViewRefactor.isEnabled()) {
+ if (v instanceof EmptyShadeView) {
+ emptyShadeVisible = true;
+ }
+ if (v instanceof FooterView) {
+ if (emptyShadeVisible || notGoneIndex == 0) {
+ // if the empty shade is visible or the footer is the first visible
+ // view, we're in a transitory state so let's leave the footer alone.
+ continue;
+ }
+ }
+ }
+
notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
if (v instanceof ExpandableNotificationRow row) {
@@ -641,7 +658,10 @@
// Incoming views have yTranslation=0 by default.
viewState.setYTranslation(algorithmState.mCurrentYPosition);
- float viewEnd = viewState.getYTranslation() + viewState.height + ambientState.getStackY();
+ float stackTop = SceneContainerFlag.isEnabled()
+ ? ambientState.getStackTop()
+ : ambientState.getStackY();
+ float viewEnd = stackTop + viewState.getYTranslation() + viewState.height;
maybeUpdateHeadsUpIsVisible(viewState, ambientState.isShadeExpanded(),
view.mustStayOnScreen(),
/* topVisible= */ viewState.getYTranslation() >= mNotificationScrimPadding,
@@ -681,7 +701,9 @@
}
} else {
if (view instanceof EmptyShadeView) {
- float fullHeight = ambientState.getLayoutMaxHeight() + mMarginBottom
+ float fullHeight = SceneContainerFlag.isEnabled()
+ ? ambientState.getStackCutoff() - ambientState.getStackTop()
+ : ambientState.getLayoutMaxHeight() + mMarginBottom
- ambientState.getStackY();
viewState.setYTranslation((fullHeight - getMaxAllowedChildHeight(view)) / 2f);
} else if (view != ambientState.getTrackedHeadsUpRow()) {
@@ -726,7 +748,7 @@
+ mPaddingBetweenElements;
setLocation(view.getViewState(), algorithmState.mCurrentYPosition, i);
- viewState.setYTranslation(viewState.getYTranslation() + ambientState.getStackY());
+ viewState.setYTranslation(viewState.getYTranslation() + stackTop);
}
@VisibleForTesting
@@ -1002,8 +1024,11 @@
// Animate pinned HUN bottom corners to and from original roundness.
final float originalCornerRadius =
row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius);
+ final float stackTop = SceneContainerFlag.isEnabled()
+ ? ambientState.getStackTop()
+ : ambientState.getStackY();
final float bottomValue = computeCornerRoundnessForPinnedHun(mHostView.getHeight(),
- ambientState.getStackY(), getMaxAllowedChildHeight(row), originalCornerRadius);
+ stackTop, getMaxAllowedChildHeight(row), originalCornerRadius);
row.requestBottomRoundness(bottomValue, STACK_SCROLL_ALGO);
row.addOnDetachResetRoundness(STACK_SCROLL_ALGO);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 634bd7e..08e81d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -20,15 +20,16 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/**
* ViewModel used by the Notification placeholders inside the scene container to update the
@@ -41,8 +42,8 @@
dumpManager: DumpManager,
private val interactor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
+ private val shadeSceneViewModel: ShadeSceneViewModel,
featureFlags: FeatureFlagsClassic,
- private val keyguardInteractor: KeyguardInteractor,
) : FlowDumperImpl(dumpManager) {
/** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
@@ -60,11 +61,19 @@
interactor.setConstrainedAvailableSpace(height)
}
+ /** Notifies that empty space on the notification scrim has been clicked. */
+ fun onEmptySpaceClicked() {
+ shadeSceneViewModel.onContentClicked()
+ }
+
/** Sets the content alpha for the current state of the brightness mirror */
fun setAlphaForBrightnessMirror(alpha: Float) {
interactor.setAlphaForBrightnessMirror(alpha)
}
+ /** Whether or not the notification scrim should be clickable. */
+ val isClickable: StateFlow<Boolean> = shadeSceneViewModel.isClickable
+
/** Corner rounding of the stack */
val shadeScrimRounding: Flow<ShadeScrimRounding> =
interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index a35709d..8e58ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -21,6 +21,7 @@
import androidx.annotation.VisibleForTesting
import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
@@ -75,6 +76,7 @@
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -136,6 +138,7 @@
private val primaryBouncerToLockscreenTransitionViewModel:
PrimaryBouncerToLockscreenTransitionViewModel,
private val aodBurnInViewModel: AodBurnInViewModel,
+ private val communalSceneInteractor: CommunalSceneInteractor,
unfoldTransitionInteractor: UnfoldTransitionInteractor,
) : FlowDumperImpl(dumpManager) {
private val statesForConstrainedNotifications: Set<KeyguardState> =
@@ -472,6 +475,17 @@
val panelAlpha = keyguardInteractor.panelAlpha
+ private fun bouncerToGoneNotificationAlpha(viewState: ViewStateAccessor): Flow<Float> =
+ merge(
+ primaryBouncerToGoneTransitionViewModel.notificationAlpha,
+ alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState),
+ )
+ .sample(communalSceneInteractor.isCommunalVisible) { alpha, isCommunalVisible ->
+ // when glanceable hub is visible, hide notifications during the transition to GONE
+ if (isCommunalVisible) 0f else alpha
+ }
+ .dumpWhileCollecting("bouncerToGoneNotificationAlpha")
+
fun keyguardAlpha(viewState: ViewStateAccessor): Flow<Float> {
// All transition view models are mututally exclusive, and safe to merge
val alphaTransitions =
@@ -479,7 +493,7 @@
keyguardInteractor.dismissAlpha.dumpWhileCollecting(
"keyguardInteractor.dismissAlpha"
),
- alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState),
+ bouncerToGoneNotificationAlpha(viewState),
aodToGoneTransitionViewModel.notificationAlpha(viewState),
aodToLockscreenTransitionViewModel.notificationAlpha,
aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
@@ -497,7 +511,6 @@
occludedToAodTransitionViewModel.lockscreenAlpha,
occludedToGoneTransitionViewModel.notificationAlpha(viewState),
occludedToLockscreenTransitionViewModel.lockscreenAlpha,
- primaryBouncerToGoneTransitionViewModel.notificationAlpha,
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 2011332..91b5d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -39,7 +39,7 @@
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.DeviceControlsController;
@@ -430,8 +430,7 @@
boolean isCasting = false;
for (CastDevice device : mCastController.getCastDevices()) {
- if (device.state == CastDevice.STATE_CONNECTED
- || device.state == CastDevice.STATE_CONNECTING) {
+ if (device.isCasting()) {
isCasting = true;
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index d75a738..0a02381 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -44,6 +44,7 @@
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
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 62c139b..28117e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -181,6 +181,7 @@
import com.android.systemui.shade.ShadeSurface;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CircleReveal;
import com.android.systemui.statusbar.CommandQueue;
@@ -1481,7 +1482,9 @@
return (v, event) -> {
mAutoHideController.checkUserAutoHide(event);
mRemoteInputManager.checkRemoteInputOutside(event);
- if (!MigrateClocksToBlueprint.isEnabled()) {
+ if (!MigrateClocksToBlueprint.isEnabled() || mQsController.isCustomizing()) {
+ // For migrate clocks flag, when the user is editing QS tiles they need to be able
+ // to touch outside the customizer to close it, such as on the status or nav bar.
mShadeController.onStatusBarTouch(event);
}
return getNotificationShadeWindowView().onTouchEvent(event);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandHeadsUpOnInlineReply.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandHeadsUpOnInlineReply.kt
new file mode 100644
index 0000000..d4e2688
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandHeadsUpOnInlineReply.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.phone
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the expand heads up on inline reply flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object ExpandHeadsUpOnInlineReply {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_EXPAND_HEADS_UP_ON_INLINE_REPLY
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.expandHeadsUpOnInlineReply()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 3925beb..c5dcb09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
+import com.android.systemui.statusbar.notification.collection.provider.OnReorderingBannedListener;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
@@ -86,7 +87,7 @@
private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
private final VisualStabilityProvider mVisualStabilityProvider;
- private final AvalancheController mAvalancheController;
+ private AvalancheController mAvalancheController;
// TODO(b/328393698) move the topHeadsUpRow logic to an interactor
private final MutableStateFlow<HeadsUpRowRepository> mTopHeadsUpRow =
@@ -175,6 +176,7 @@
javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(),
this::onShadeOrQsExpanded);
}
+ mVisualStabilityProvider.addPersistentReorderingBannedListener(mOnReorderingBannedListener);
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -382,6 +384,8 @@
private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
+ mAvalancheController.setEnableAtRuntime(true);
+
for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
if (isHeadsUpEntry(entry.getKey())) {
// Maybe the heads-up was removed already
@@ -392,6 +396,28 @@
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
};
+ private final OnReorderingBannedListener mOnReorderingBannedListener = () -> {
+ if (mAvalancheController != null) {
+ // Waiting HUNs in AvalancheController are still promoted to the HUN section and thus
+ // seen in open shade; clear them so we don't show them again when the shade closes and
+ // reordering is allowed again.
+ mAvalancheController.logDroppedHuns(mAvalancheController.getWaitingKeys().size());
+ mAvalancheController.clearNext();
+
+ // In open shade the first HUN is pinned, and visual stability logic prevents us from
+ // unpinning this first HUN as long as the shade remains open. AvalancheController only
+ // shows the next HUN when the currently showing HUN is unpinned, so we must disable
+ // throttling here so that the incoming HUN stream is not forever paused. This is reset
+ // when reorder becomes allowed.
+ mAvalancheController.setEnableAtRuntime(false);
+
+ // Note that we cannot do the above when
+ // 1) the remove runnable runs because its delay means it may not run before shade close
+ // 2) reordering is allowed again (when shade closes) because the HUN appear animation
+ // will have started by then
+ }
+ };
+
///////////////////////////////////////////////////////////////////////////////////////////////
// HeadsUpManager utility (protected) methods overrides:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
index 8a45ec1..4aece3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
@@ -42,7 +42,7 @@
}
override fun onViewAttached() {
- if (!smartspaceRelocateToBottom() || !smartspaceController.isEnabled()) {
+ if (!smartspaceRelocateToBottom() || !smartspaceController.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index e400ab6..e96326a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -34,6 +34,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.Flags.communalHub
+import com.android.systemui.Flags.mediaLockscreenLaunchAnimation
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DelegateTransitionAnimatorController
import com.android.systemui.assist.AssistManager
@@ -247,9 +248,10 @@
val actuallyShowOverLockscreen =
showOverLockscreen &&
intent.isActivity &&
- (skipLockscreenChecks || activityIntentHelper.wouldPendingShowOverLockscreen(
- intent,
- lockScreenUserManager.currentUserId
+ (skipLockscreenChecks ||
+ activityIntentHelper.wouldPendingShowOverLockscreen(
+ intent,
+ lockScreenUserManager.currentUserId
))
val animate =
@@ -470,12 +472,16 @@
shadeControllerLazy.get().collapseShadeForActivityStart()
}
if (communalHub()) {
- communalSceneInteractor.snapToSceneForActivityStart(CommunalScenes.Blank)
+ communalSceneInteractor.changeSceneForActivityStartOnDismissKeyguard()
}
return deferred
}
override fun willRunAnimationOnKeyguard(): Boolean {
+ if (communalHub() && communalSceneInteractor.isIdleOnCommunal.value) {
+ // Override to false when launching activity over the hub that requires auth
+ return false
+ }
return willAnimateOnKeyguard
}
}
@@ -557,7 +563,7 @@
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
super.onTransitionAnimationStart(isExpandingFullyAbove)
if (communalHub()) {
- communalSceneInteractor.snapToSceneForActivityStart(
+ communalSceneInteractor.snapToScene(
CommunalScenes.Blank,
ActivityTransitionAnimator.TIMINGS.totalDuration
)
@@ -630,8 +636,9 @@
isActivityIntent: Boolean,
showOverLockscreen: Boolean,
): Boolean {
- // TODO(b/294418322): Support launch animations when occluded.
- if (keyguardStateController.isOccluded) {
+ // TODO(b/294418322): always support launch animations when occluded.
+ val ignoreOcclusion = showOverLockscreen && mediaLockscreenLaunchAnimation()
+ if (keyguardStateController.isOccluded && !ignoreOcclusion) {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index eec617b..a33996b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -19,8 +19,8 @@
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import android.content.Context;
import android.graphics.Rect;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 3784132..3ba62b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -44,6 +44,7 @@
import androidx.lifecycle.Observer;
+import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
@@ -57,12 +58,13 @@
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.res.R;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -354,7 +356,11 @@
mProvisionedController.addCallback(this);
mCurrentUserSetup = mProvisionedController.isCurrentUserSetup();
mZenController.addCallback(this);
- mCast.addCallback(mCastCallback);
+ if (!Flags.statusBarScreenSharingChips()) {
+ // If the flag is enabled, the cast icon is handled in the new screen sharing chips
+ // instead of here so we don't need to listen for events here.
+ mCast.addCallback(mCastCallback);
+ }
mHotspot.addCallback(mHotspotCallback);
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
@@ -362,7 +368,11 @@
mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
- mRecordingController.addCallback(this);
+ if (!Flags.statusBarScreenSharingChips()) {
+ // If the flag is enabled, the screen record icon is handled in the new screen sharing
+ // chips instead of here so we don't need to listen for events here.
+ mRecordingController.addCallback(this);
+ }
mJavaAdapter.alwaysCollectFlow(mConnectedDisplayInteractor.getConnectedDisplayState(),
this::onConnectedDisplayAvailabilityChanged);
@@ -519,10 +529,14 @@
}
private void updateCast() {
+ if (Flags.statusBarScreenSharingChips()) {
+ // The cast icon is handled in the new screen sharing chips instead of here.
+ return;
+ }
+
boolean isCasting = false;
for (CastDevice device : mCast.getCastDevices()) {
- if (device.state == CastDevice.STATE_CONNECTING
- || device.state == CastDevice.STATE_CONNECTED) {
+ if (device.isCasting()) {
isCasting = true;
break;
}
@@ -789,6 +803,10 @@
private Runnable mRemoveCastIconRunnable = new Runnable() {
@Override
public void run() {
+ if (Flags.statusBarScreenSharingChips()) {
+ // The cast icon is handled in the new screen sharing chips instead of here.
+ return;
+ }
if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
mIconController.setIconVisibility(mSlotCast, false);
}
@@ -797,8 +815,13 @@
// Screen Recording
@Override
public void onCountdown(long millisUntilFinished) {
+ if (Flags.statusBarScreenSharingChips()) {
+ // The screen record icon is handled in the new screen sharing chips instead of here.
+ return;
+ }
if (DEBUG) Log.d(TAG, "screenrecord: countdown " + millisUntilFinished);
- int countdown = (int) Math.floorDiv(millisUntilFinished + 500, 1000);
+ int countdown =
+ (int) ScreenRecordModel.Starting.Companion.toCountdownSeconds(millisUntilFinished);
int resourceId = R.drawable.stat_sys_screen_record;
String description = Integer.toString(countdown);
switch (countdown) {
@@ -821,6 +844,10 @@
@Override
public void onCountdownEnd() {
+ if (Flags.statusBarScreenSharingChips()) {
+ // The screen record icon is handled in the new screen sharing chips instead of here.
+ return;
+ }
if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown");
mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
// Reset talkback priority
@@ -830,6 +857,10 @@
@Override
public void onRecordingStart() {
+ if (Flags.statusBarScreenSharingChips()) {
+ // The screen record icon is handled in the new screen sharing chips instead of here.
+ return;
+ }
if (DEBUG) Log.d(TAG, "screenrecord: showing icon");
mIconController.setIcon(mSlotScreenRecord,
R.drawable.stat_sys_screen_record,
@@ -839,6 +870,10 @@
@Override
public void onRecordingEnd() {
+ if (Flags.statusBarScreenSharingChips()) {
+ // The screen record icon is handled in the new screen sharing chips instead of here.
+ return;
+ }
// Ensure this is on the main thread
if (DEBUG) Log.d(TAG, "screenrecord: hiding icon");
mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index ae3f923..6676a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -23,6 +23,7 @@
import android.view.View;
import com.android.systemui.res.R;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
public final class PhoneStatusBarTransitions extends BarTransitions {
private static final float ICON_ALPHA_WHEN_NOT_OPAQUE = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
index 29c1372..25b8bfe0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_WARNING;
import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_VIEW;
import android.annotation.NonNull;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 4505a1d..b1754fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -192,27 +192,42 @@
if (!deferBouncer && mKeyguardStateController.isShowing()) {
onLockedRemoteInput(row, clickedView);
} else {
- if (row.isChildInGroup() && !row.areChildrenExpanded()) {
- // The group isn't expanded, let's make sure it's visible!
- mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
- }
-
- if (android.app.Flags.compactHeadsUpNotificationReply()
- && row.isCompactConversationHeadsUpOnScreen()) {
- // Notification can be system expanded true and it is set user expanded in
- // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType doesn't
- // change. To expand huning notification properly, we need set userExpanded false.
- if (!row.isPinned() && row.isExpanded()) {
- row.setUserExpanded(false);
+ if (ExpandHeadsUpOnInlineReply.isEnabled()) {
+ if (row.isChildInGroup() && !row.areChildrenExpanded()) {
+ // The group isn't expanded, let's make sure it's visible!
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ } else if (!row.isChildInGroup() && !row.isExpanded()) {
+ // notification isn't expanded, let's make sure it's visible!
+ row.toggleExpansionState();
+ row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
- // expand notification emits expanded information to HUN listener.
- row.expandNotification();
} else {
- // Note: Since Normal HUN has remote input view in it, we don't expect to hit
- // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN.
- row.setUserExpanded(true);
+ if (row.isChildInGroup() && !row.areChildrenExpanded()) {
+ // The group isn't expanded, let's make sure it's visible!
+ mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
+ }
+
+ if (android.app.Flags.compactHeadsUpNotificationReply()
+ && row.isCompactConversationHeadsUpOnScreen()) {
+ // Notification can be system expanded true and it is set user expanded in
+ // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType
+ // doesn't change. To expand huning notification properly,
+ // we need set userExpanded false.
+ if (!row.isPinned() && row.isExpanded()) {
+ row.setUserExpanded(false);
+ }
+ // expand notification emits expanded information to HUN listener.
+ row.expandNotification();
+ } else {
+ // TODO(b/346976443) Group and normal notification expansions are two different
+ // concepts. We should never call setUserExpanded for expanding groups.
+
+ // Note: Since Normal HUN has remote input view in it, we don't expect to hit
+ // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN.
+ row.setUserExpanded(true);
+ }
+ row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
- row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 4b1ee58..0218784 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -68,7 +68,7 @@
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
- private boolean mIsIdleOnGone = false;
+ private boolean mIsIdleOnGone = true;
private boolean mShouldAdjustInsets = false;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
@@ -282,7 +282,7 @@
// since we don't want stray touches to go through the light reveal scrim to whatever is
// underneath.
return mIsStatusBarExpanded
- || !mIsIdleOnGone
+ || (SceneContainerFlag.isEnabled() && !mIsIdleOnGone)
|| mPrimaryBouncerInteractor.isShowing().getValue()
|| mAlternateBouncerInteractor.isVisibleState()
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index 50649a8..2c48487 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -29,7 +29,8 @@
* @property startTimeMs the time that the phone call started, based on the notification's
* `when` field. Importantly, this time is relative to
* [com.android.systemui.util.time.SystemClock.currentTimeMillis], **not**
- * [com.android.systemui.util.time.SystemClock.elapsedRealtime].
+ * [com.android.systemui.util.time.SystemClock.elapsedRealtime]. This value can be 0 if the
+ * user has started an outgoing call that hasn't been answered yet - see b/192379214.
* @property intent the intent associated with the call notification.
*/
data class InCall(val startTimeMs: Long, val intent: PendingIntent?) : OngoingCallModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index ec3af87..a7c5f78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -345,32 +345,67 @@
orElse = flowOf(false),
retrySignal = telephonyProcessCrashedEvent,
)
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ .stateIn(scope, SharingStarted.Eagerly, false)
private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> =
conflatedCallbackFlow {
- val callback = SatelliteProvisionStateCallback { provisioned ->
- logBuffer.i {
- "onSatelliteProvisionStateChanged: " +
- if (provisioned) "provisioned" else "not provisioned"
+ // TODO(b/347992038): SatelliteManager should be sending the current provisioned
+ // status when we register a callback. Until then, we have to manually query here.
+
+ // First, check to see what the current status is, and send the result to the output
+ trySend(queryIsSatelliteProvisioned(sm))
+
+ val callback = SatelliteProvisionStateCallback { provisioned ->
+ logBuffer.i {
+ "onSatelliteProvisionStateChanged: " +
+ if (provisioned) "provisioned" else "not provisioned"
+ }
+ trySend(provisioned)
}
- trySend(provisioned)
- }
- var registered = false
- try {
- sm.registerForProvisionStateChanged(
- bgDispatcher.asExecutor(),
- callback,
- )
- registered = true
- } catch (e: Exception) {
- logBuffer.e("error registering for provisioning state callback", e)
- }
+ var registered = false
+ try {
+ logBuffer.i { "registerForProvisionStateChanged" }
+ sm.registerForProvisionStateChanged(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
+ registered = true
+ } catch (e: Exception) {
+ logBuffer.e("error registering for provisioning state callback", e)
+ }
- awaitClose {
- if (registered) {
- sm.unregisterForProvisionStateChanged(callback)
+ awaitClose {
+ if (registered) {
+ sm.unregisterForProvisionStateChanged(callback)
+ }
+ }
+ }
+ .flowOn(bgDispatcher)
+
+ /** Check the current satellite provisioning status. */
+ private suspend fun queryIsSatelliteProvisioned(sm: SupportedSatelliteManager): Boolean =
+ withContext(bgDispatcher) {
+ suspendCancellableCoroutine { continuation ->
+ val receiver =
+ object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+ override fun onResult(result: Boolean) {
+ logBuffer.i { "requestIsProvisioned.onResult: $result" }
+ continuation.resume(result)
+ }
+
+ override fun onError(exception: SatelliteManager.SatelliteException) {
+ logBuffer.e("requestIsProvisioned.onError:", exception)
+ continuation.resume(false)
+ }
+ }
+
+ logBuffer.i { "Query for current satellite provisioned state." }
+ try {
+ sm.requestIsProvisioned(bgDispatcher.asExecutor(), receiver)
+ } catch (e: Exception) {
+ logBuffer.e("Exception while calling SatelliteManager.requestIsProvisioned:", e)
+ continuation.resume(false)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 7644653..de36e40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -18,8 +18,11 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
+import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
import android.view.View
import android.widget.ImageView
+import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Flags
@@ -29,6 +32,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
@@ -84,23 +88,45 @@
if (Flags.statusBarScreenSharingChips()) {
val chipView: View = view.requireViewById(R.id.ongoing_activity_chip)
+ val chipContext = chipView.context
val chipIconView: ImageView =
chipView.requireViewById(R.id.ongoing_activity_chip_icon)
val chipTimeView: ChipChronometer =
chipView.requireViewById(R.id.ongoing_activity_chip_time)
+ val chipTextView: TextView =
+ chipView.requireViewById(R.id.ongoing_activity_chip_text)
+ val chipBackgroundView =
+ chipView.requireViewById<ChipBackgroundContainer>(
+ R.id.ongoing_activity_chip_background
+ )
launch {
viewModel.ongoingActivityChip.collect { chipModel ->
when (chipModel) {
is OngoingActivityChipModel.Shown -> {
- IconViewBinder.bind(chipModel.icon, chipIconView)
- ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+ // Data
+ IconViewBinder.bindNullable(chipModel.icon, chipIconView)
+ setChipMainContent(chipModel, chipTextView, chipTimeView)
chipView.setOnClickListener(chipModel.onClickListener)
+ // Accessibility
+ setChipAccessibility(chipModel, chipView)
+
+ // Colors
+ val textColor = chipModel.colors.text(chipContext)
+ chipIconView.imageTintList = ColorStateList.valueOf(textColor)
+ chipTimeView.setTextColor(textColor)
+ chipTextView.setTextColor(textColor)
+ (chipBackgroundView.background as GradientDrawable).color =
+ chipModel.colors.background(chipContext)
+
+ // Notify listeners
listener.onOngoingActivityStatusChanged(
hasOngoingActivity = true
)
}
is OngoingActivityChipModel.Hidden -> {
+ // The Chronometer should be stopped to prevent leaks -- see
+ // b/192243808 and [Chronometer.start].
chipTimeView.stop()
listener.onOngoingActivityStatusChanged(
hasOngoingActivity = false
@@ -114,6 +140,81 @@
}
}
+ private fun setChipMainContent(
+ chipModel: OngoingActivityChipModel.Shown,
+ chipTextView: TextView,
+ chipTimeView: ChipChronometer,
+ ) {
+ when (chipModel) {
+ is OngoingActivityChipModel.Shown.Countdown -> {
+ chipTextView.text = chipModel.secondsUntilStarted.toString()
+ chipTextView.visibility = View.VISIBLE
+
+ // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
+ // [Chronometer.start].
+ chipTimeView.stop()
+ chipTimeView.visibility = View.GONE
+ }
+ is OngoingActivityChipModel.Shown.Timer -> {
+ ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+ chipTimeView.visibility = View.VISIBLE
+
+ chipTextView.visibility = View.GONE
+ }
+ is OngoingActivityChipModel.Shown.IconOnly -> {
+ chipTextView.visibility = View.GONE
+ // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
+ // [Chronometer.start].
+ chipTimeView.stop()
+ chipTimeView.visibility = View.GONE
+ }
+ }
+ updateChipTextPadding(chipModel, chipTextView, chipTimeView)
+ }
+
+ private fun updateChipTextPadding(
+ chipModel: OngoingActivityChipModel.Shown,
+ chipTextView: TextView,
+ chipTimeView: ChipChronometer,
+ ) {
+ val requiresPadding = chipModel.icon != null
+ if (requiresPadding) {
+ chipTextView.addChipTextPaddingStart()
+ chipTimeView.addChipTextPaddingStart()
+ } else {
+ chipTextView.removeChipTextPaddingStart()
+ chipTimeView.removeChipTextPaddingStart()
+ }
+ }
+
+ private fun View.addChipTextPaddingStart() {
+ this.setPaddingRelative(
+ this.context.resources.getDimensionPixelSize(
+ R.dimen.ongoing_activity_chip_icon_text_padding
+ ),
+ paddingTop,
+ paddingEnd,
+ paddingBottom,
+ )
+ }
+
+ private fun View.removeChipTextPaddingStart() {
+ this.setPaddingRelative(/* start= */ 0, paddingTop, paddingEnd, paddingBottom)
+ }
+
+ private fun setChipAccessibility(chipModel: OngoingActivityChipModel.Shown, chipView: View) {
+ when (chipModel) {
+ is OngoingActivityChipModel.Shown.Countdown -> {
+ // Set as assertive so talkback will announce the countdown
+ chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+ }
+ is OngoingActivityChipModel.Shown.Timer,
+ is OngoingActivityChipModel.Shown.IconOnly -> {
+ chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
+ }
+ }
+ }
+
private fun animateLightsOutView(view: View, visible: Boolean) {
view.animate().cancel()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 32774e0..dbe54f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -39,6 +39,7 @@
private val tag = "AvalancheController"
private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG)
+ var enableAtRuntime = true
// HUN showing right now, in the floating state where full shade is hidden, on launcher or AOD
@VisibleForTesting var headsUpEntryShowing: HeadsUpEntry? = null
@@ -81,13 +82,17 @@
dumpManager.registerNormalDumpable(tag, /* module */ this)
}
+ fun isEnabled() : Boolean {
+ return NotificationThrottleHun.isEnabled && enableAtRuntime
+ }
+
fun getShowingHunKey(): String {
return getKey(headsUpEntryShowing)
}
/** Run or delay Runnable for given HeadsUpEntry */
fun update(entry: HeadsUpEntry?, runnable: Runnable, label: String) {
- if (!NotificationThrottleHun.isEnabled) {
+ if (!isEnabled()) {
runnable.run()
return
}
@@ -143,7 +148,7 @@
* all Runnables associated with that entry.
*/
fun delete(entry: HeadsUpEntry?, runnable: Runnable, label: String) {
- if (!NotificationThrottleHun.isEnabled) {
+ if (!isEnabled()) {
runnable.run()
return
}
@@ -184,7 +189,7 @@
* BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration.
*/
fun getDurationMs(entry: HeadsUpEntry, autoDismissMs: Int): Int {
- if (!NotificationThrottleHun.isEnabled) {
+ if (!isEnabled()) {
// Use default duration, like we did before AvalancheController existed
return autoDismissMs
}
@@ -233,7 +238,7 @@
/** Return true if entry is waiting to show. */
fun isWaiting(key: String): Boolean {
- if (!NotificationThrottleHun.isEnabled) {
+ if (!isEnabled()) {
return false
}
for (entry in nextMap.keys) {
@@ -246,7 +251,7 @@
/** Return list of keys for huns waiting */
fun getWaitingKeys(): MutableList<String> {
- if (!NotificationThrottleHun.isEnabled) {
+ if (!isEnabled()) {
return mutableListOf()
}
val keyList = mutableListOf<String>()
@@ -257,7 +262,7 @@
}
fun getWaitingEntry(key: String): HeadsUpEntry? {
- if (!NotificationThrottleHun.isEnabled) {
+ if (!isEnabled()) {
return null
}
for (headsUpEntry in nextMap.keys) {
@@ -269,7 +274,7 @@
}
fun getWaitingEntryList(): List<HeadsUpEntry> {
- if (!NotificationThrottleHun.isEnabled) {
+ if (!isEnabled()) {
return mutableListOf()
}
return nextMap.keys.toList()
@@ -310,11 +315,7 @@
// Remove runnable labels for dropped huns
val listToDrop = nextList.subList(1, nextList.size)
-
- // Log dropped HUNs
- for (e in listToDrop) {
- uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED)
- }
+ logDroppedHuns(listToDrop.size)
if (debug) {
// Clear runnable labels
@@ -331,6 +332,12 @@
showNow(headsUpEntryShowing!!, headsUpEntryShowingRunnableList)
}
+ fun logDroppedHuns(numDropped: Int) {
+ for (n in 1..numDropped) {
+ uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED)
+ }
+ }
+
fun clearNext() {
nextList.clear()
nextMap.clear()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index abedd3220..a3dcc3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -37,15 +37,4 @@
void onCastDevicesChanged();
}
- public static final class CastDevice {
- public static final int STATE_DISCONNECTED = 0;
- public static final int STATE_CONNECTING = 1;
- public static final int STATE_CONNECTED = 2;
-
- public String id;
- public String name;
- public String description;
- public int state = STATE_DISCONNECTED;
- public Object tag;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 64bdf60..45cb52a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -19,15 +19,12 @@
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -37,8 +34,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.res.R;
-import com.android.systemui.util.Utils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -51,10 +46,11 @@
/** Platform implementation of the cast controller. **/
@SysUISingleton
public class CastControllerImpl implements CastController {
- private static final String TAG = "CastController";
+ public static final String TAG = "CastController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
+ private final PackageManager mPackageManager;
@GuardedBy("mCallbacks")
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final MediaRouter mMediaRouter;
@@ -68,8 +64,12 @@
private MediaProjectionInfo mProjection;
@Inject
- public CastControllerImpl(Context context, DumpManager dumpManager) {
+ public CastControllerImpl(
+ Context context,
+ PackageManager packageManager,
+ DumpManager dumpManager) {
mContext = context;
+ mPackageManager = packageManager;
mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouter.setRouterGroupId(MediaRouter.MIRRORING_GROUP_ID);
mProjectionManager = (MediaProjectionManager)
@@ -156,36 +156,17 @@
final ArrayList<CastDevice> devices = new ArrayList<>();
synchronized(mRoutes) {
for (RouteInfo route : mRoutes.values()) {
- final CastDevice device = new CastDevice();
- device.id = route.getTag().toString();
- final CharSequence name = route.getName(mContext);
- device.name = name != null ? name.toString() : null;
- final CharSequence description = route.getDescription();
- device.description = description != null ? description.toString() : null;
-
- int statusCode = route.getStatusCode();
- if (statusCode == RouteInfo.STATUS_CONNECTING) {
- device.state = CastDevice.STATE_CONNECTING;
- } else if (route.isSelected() || statusCode == RouteInfo.STATUS_CONNECTED) {
- device.state = CastDevice.STATE_CONNECTED;
- } else {
- device.state = CastDevice.STATE_DISCONNECTED;
- }
-
- device.tag = route;
- devices.add(device);
+ devices.add(CastDevice.Companion.toCastDevice(route, mContext));
}
}
synchronized (mProjectionLock) {
if (mProjection != null) {
- final CastDevice device = new CastDevice();
- device.id = mProjection.getPackageName();
- device.name = getAppName(mProjection.getPackageName());
- device.description = mContext.getString(R.string.quick_settings_casting);
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mProjection;
- devices.add(device);
+ devices.add(
+ CastDevice.Companion.toCastDevice(
+ mProjection,
+ mContext,
+ mPackageManager));
}
}
@@ -194,18 +175,18 @@
@Override
public void startCasting(CastDevice device) {
- if (device == null || device.tag == null) return;
- final RouteInfo route = (RouteInfo) device.tag;
+ if (device == null || device.getTag() == null) return;
+ final RouteInfo route = (RouteInfo) device.getTag();
if (DEBUG) Log.d(TAG, "startCasting: " + routeToString(route));
mMediaRouter.selectRoute(ROUTE_TYPE_REMOTE_DISPLAY, route);
}
@Override
public void stopCasting(CastDevice device) {
- final boolean isProjection = device.tag instanceof MediaProjectionInfo;
+ final boolean isProjection = device.getTag() instanceof MediaProjectionInfo;
if (DEBUG) Log.d(TAG, "stopCasting isProjection=" + isProjection);
if (isProjection) {
- final MediaProjectionInfo projection = (MediaProjectionInfo) device.tag;
+ final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag();
if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) {
mProjectionManager.stopActiveProjection();
} else {
@@ -219,7 +200,7 @@
@Override
public boolean hasConnectedCastDevice() {
return getCastDevices().stream().anyMatch(
- castDevice -> castDevice.state == CastDevice.STATE_CONNECTED);
+ castDevice -> castDevice.getState() == CastDevice.CastState.Connected);
}
private void setProjection(MediaProjectionInfo projection, boolean started) {
@@ -241,27 +222,6 @@
}
}
- private String getAppName(String packageName) {
- final PackageManager pm = mContext.getPackageManager();
- if (Utils.isHeadlessRemoteDisplayProvider(pm, packageName)) {
- return "";
- }
-
- try {
- final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
- if (appInfo != null) {
- final CharSequence label = appInfo.loadLabel(pm);
- if (!TextUtils.isEmpty(label)) {
- return label.toString();
- }
- }
- Log.w(TAG, "No label found for package: " + packageName);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Error getting appName for package: " + packageName, e);
- }
- return packageName;
- }
-
private void updateRemoteDisplays() {
synchronized(mRoutes) {
mRoutes.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt
new file mode 100644
index 0000000..5fc160b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.policy
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.media.MediaRouter
+import android.media.projection.MediaProjectionInfo
+import android.text.TextUtils
+import android.util.Log
+import com.android.systemui.res.R
+import com.android.systemui.util.Utils
+
+/** Represents a specific cast session. */
+data class CastDevice(
+ val id: String,
+ /** A human-readable name of what is receiving the cast (e.g. "Home Speaker", "Abc App"). */
+ val name: String?,
+ /** An optional description with more information about the cast. */
+ val description: String? = null,
+ val state: CastState,
+ val origin: CastOrigin,
+ /** Optional tag to use as a comparison value between cast sessions. */
+ val tag: Any? = null,
+) {
+ val isCasting = state == CastState.Connecting || state == CastState.Connected
+
+ companion object {
+ /** Creates a [CastDevice] based on the provided information from MediaRouter. */
+ fun MediaRouter.RouteInfo.toCastDevice(context: Context): CastDevice {
+ val state =
+ when {
+ statusCode == MediaRouter.RouteInfo.STATUS_CONNECTING -> CastState.Connecting
+ this.isSelected || statusCode == MediaRouter.RouteInfo.STATUS_CONNECTED ->
+ CastState.Connected
+ else -> CastState.Disconnected
+ }
+ return CastDevice(
+ id = this.tag.toString(),
+ name = this.getName(context)?.toString(),
+ description = this.description?.toString(),
+ state = state,
+ tag = this,
+ origin = CastOrigin.MediaRouter,
+ )
+ }
+
+ /** Creates a [CastDevice] based on the provided information from MediaProjection. */
+ fun MediaProjectionInfo.toCastDevice(
+ context: Context,
+ packageManager: PackageManager
+ ): CastDevice {
+ return CastDevice(
+ id = this.packageName,
+ name = getAppName(this.packageName, packageManager),
+ description = context.getString(R.string.quick_settings_casting),
+ state = CastState.Connected,
+ tag = this,
+ origin = CastOrigin.MediaProjection,
+ )
+ }
+
+ private fun getAppName(packageName: String, packageManager: PackageManager): String {
+ if (Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)) {
+ return ""
+ }
+ try {
+ val appInfo = packageManager.getApplicationInfo(packageName, 0)
+ val label = appInfo.loadLabel(packageManager)
+ if (!TextUtils.isEmpty(label)) {
+ return label.toString()
+ }
+ Log.w(CastControllerImpl.TAG, "No label found for package: $packageName")
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(CastControllerImpl.TAG, "Error getting appName for package: $packageName", e)
+ }
+ return packageName
+ }
+ }
+
+ enum class CastState {
+ Disconnected,
+ Connecting,
+ Connected,
+ }
+
+ enum class CastOrigin {
+ /** SysUI found out about this cast device from MediaRouter APIs. */
+ MediaRouter,
+ /** SysUI found out about this cast device from MediaProjection APIs. */
+ MediaProjection,
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index de0eb49..528ef49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -31,8 +31,8 @@
import kotlin.Unit;
-import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -42,7 +42,13 @@
public class DevicePostureControllerImpl implements DevicePostureController {
/** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
private static final int COMMON_STATE_USE_BASE_STATE = 1000;
- private final List<Callback> mListeners = new ArrayList<>();
+ /**
+ * Despite this is always used only from the main thread, it might be that some listener
+ * unregisters itself while we're sending the update, ending up modifying this while we're
+ * iterating it.
+ * Keeping a threadsafe list of listeners helps preventing ConcurrentModificationExceptions.
+ */
+ private final List<Callback> mListeners = new CopyOnWriteArrayList<>();
private final List<DeviceState> mSupportedStates;
private DeviceState mCurrentDeviceState;
private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 1fc7bf4..31776cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -115,7 +115,7 @@
private final SendButtonTextWatcher mTextWatcher;
private final TextView.OnEditorActionListener mEditorActionHandler;
private final ArrayList<Runnable> mOnSendListeners = new ArrayList<>();
- private final ArrayList<Consumer<Boolean>> mOnVisibilityChangedListeners = new ArrayList<>();
+ private Consumer<Boolean> mOnVisibilityChangedListener = null;
private final ArrayList<OnFocusChangeListener> mEditTextFocusChangeListeners =
new ArrayList<>();
@@ -733,24 +733,17 @@
* {@link #getVisibility()} would return {@link View#VISIBLE}, and {@code false} it would return
* any other value.
*/
- public void addOnVisibilityChangedListener(Consumer<Boolean> listener) {
- mOnVisibilityChangedListeners.add(listener);
- }
-
- /**
- * Unregister a listener previously registered via
- * {@link #addOnVisibilityChangedListener(Consumer)}.
- */
- public void removeOnVisibilityChangedListener(Consumer<Boolean> listener) {
- mOnVisibilityChangedListeners.remove(listener);
+ public void setOnVisibilityChangedListener(Consumer<Boolean> listener) {
+ mOnVisibilityChangedListener = listener;
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (changedView == this) {
- for (Consumer<Boolean> listener : new ArrayList<>(mOnVisibilityChangedListeners)) {
- listener.accept(visibility == VISIBLE);
+ final Consumer<Boolean> visibilityChangedListener = mOnVisibilityChangedListener;
+ if (visibilityChangedListener != null) {
+ visibilityChangedListener.accept(visibility == VISIBLE);
}
// Hide soft-keyboard when the input view became invisible
// (i.e. The notification shade collapsed by pressing the home key)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index ad19729..23e40b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -24,7 +24,14 @@
import android.service.notification.ZenModeConfig.ZenRule;
import com.android.systemui.statusbar.policy.ZenModeController.Callback;
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor;
+/**
+ * Callback-based controller for listening to (or making) zen mode changes. Please prefer using the
+ * Flow-based {@link ZenModeInteractor} for new code instead of this.
+ *
+ * TODO(b/308591859): This should eventually be replaced by ZenModeInteractor/ZenModeRepository.
+ */
public interface ZenModeController extends CallbackController<Callback> {
void setZen(int zen, Uri conditionId, String reason);
int getZen();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 15200bd..e08e4d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -73,7 +73,6 @@
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepository;
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepositoryImpl;
import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepositoryModule;
-import com.android.systemui.statusbar.policy.data.repository.ZenModeRepositoryModule;
import dagger.Binds;
import dagger.Module;
@@ -84,7 +83,7 @@
import javax.inject.Named;
/** Dagger Module for code in the statusbar.policy package. */
-@Module(includes = { DeviceProvisioningRepositoryModule.class, ZenModeRepositoryModule.class })
+@Module(includes = {DeviceProvisioningRepositoryModule.class})
public interface StatusBarPolicyModule {
String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt
deleted file mode 100644
index 94ab58a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepository.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy.data.repository
-
-import android.app.NotificationManager
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.statusbar.policy.ZenModeController
-import dagger.Binds
-import dagger.Module
-import javax.inject.Inject
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-
-/**
- * A repository that holds information about the status and configuration of Zen Mode (or Do Not
- * Disturb/DND Mode).
- */
-interface ZenModeRepository {
- val zenMode: Flow<Int>
- val consolidatedNotificationPolicy: Flow<NotificationManager.Policy?>
-}
-
-class ZenModeRepositoryImpl
-@Inject
-constructor(
- private val zenModeController: ZenModeController,
-) : ZenModeRepository {
- // TODO(b/308591859): ZenModeController should use flows instead of callbacks. The
- // conflatedCallbackFlows here should be replaced eventually, see:
- // https://docs.google.com/document/d/1gAiuYupwUAFdbxkDXa29A4aFNu7XoCd7sCIk31WTnHU/edit?resourcekey=0-J4ZBiUhLhhQnNobAcI2vIw
-
- override val zenMode: Flow<Int> = conflatedCallbackFlow {
- val callback =
- object : ZenModeController.Callback {
- override fun onZenChanged(zen: Int) {
- trySend(zen)
- }
- }
- zenModeController.addCallback(callback)
- trySend(zenModeController.zen)
-
- awaitClose { zenModeController.removeCallback(callback) }
- }
-
- override val consolidatedNotificationPolicy: Flow<NotificationManager.Policy?> =
- conflatedCallbackFlow {
- val callback =
- object : ZenModeController.Callback {
- override fun onConsolidatedPolicyChanged(policy: NotificationManager.Policy?) {
- trySend(policy)
- }
- }
- zenModeController.addCallback(callback)
- trySend(zenModeController.consolidatedPolicy)
-
- awaitClose { zenModeController.removeCallback(callback) }
- }
-}
-
-@Module
-interface ZenModeRepositoryModule {
- @Binds fun bindImpl(impl: ZenModeRepositoryImpl): ZenModeRepository
-}
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 ae31851..f5d7d00 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
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.policy.domain.interactor
import android.provider.Settings
-import com.android.systemui.statusbar.policy.data.repository.ZenModeRepository
+import com.android.settingslib.statusbar.notification.data.repository.ZenModeRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -30,9 +30,9 @@
*/
class ZenModeInteractor @Inject constructor(repository: ZenModeRepository) {
val isZenModeEnabled: Flow<Boolean> =
- repository.zenMode
+ repository.globalZenMode
.map {
- when (it) {
+ when (it ?: Settings.Global.ZEN_MODE_OFF) {
Settings.Global.ZEN_MODE_ALARMS -> true
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS -> true
Settings.Global.ZEN_MODE_NO_INTERRUPTIONS -> true
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index 2797b8d..e9f4374 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -36,10 +36,10 @@
import com.android.internal.logging.InstanceId
import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.DebugLogger.debugLog
+import com.android.systemui.res.R
import com.android.systemui.shared.hardware.hasInputDevice
import com.android.systemui.shared.hardware.isAnyStylusSource
import com.android.systemui.util.NotificationChannels
@@ -65,8 +65,10 @@
private var batteryCapacity = 1.0f
private var suppressed = false
private var instanceId: InstanceId? = null
- @VisibleForTesting var inputDeviceId: Int? = null
- private set
+ @VisibleForTesting
+ var inputDeviceId: Int? = null
+ private set
+
@VisibleForTesting var instanceIdSequence = InstanceIdSequence(1 shl 13)
fun init() {
@@ -113,7 +115,7 @@
inputDeviceId = deviceId
if (batteryState.capacity == batteryCapacity || batteryState.capacity <= 0f)
return@updateBattery
-
+ // Note that batteryState.capacity == NaN will fall through to here
batteryCapacity = batteryState.capacity
debugLog {
"Updating notification battery state to $batteryCapacity " +
@@ -172,7 +174,7 @@
}
private fun isBatteryBelowThreshold(): Boolean {
- return batteryCapacity <= LOW_BATTERY_THRESHOLD
+ return !batteryCapacity.isNaN() && batteryCapacity <= LOW_BATTERY_THRESHOLD
}
private fun hasConnectedBluetoothStylus(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
copy to packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
index 44f1c1e..b6c2ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
@@ -14,45 +14,37 @@
* limitations under the License.
*/
-package com.android.systemui.keyboard.shortcut.domain.interactor
+package com.android.systemui.touchpad.tutorial.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
import com.android.systemui.model.SysUiState
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.QuickStepContract
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
@SysUISingleton
-class ShortcutHelperInteractor
+class TouchpadGesturesInteractor
@Inject
constructor(
- private val displayTracker: DisplayTracker,
- @Background private val backgroundScope: CoroutineScope,
private val sysUiState: SysUiState,
- private val repository: ShortcutHelperRepository
+ private val displayTracker: DisplayTracker,
+ @Background private val backgroundScope: CoroutineScope
) {
-
- val state: Flow<ShortcutHelperState> = repository.state
-
- fun onViewClosed() {
- repository.hide()
- setSysUiStateFlagEnabled(false)
+ fun disableGestures() {
+ setGesturesState(disabled = true)
}
- fun onViewOpened() {
- setSysUiStateFlagEnabled(true)
+ fun enableGestures() {
+ setGesturesState(disabled = false)
}
- private fun setSysUiStateFlagEnabled(enabled: Boolean) {
+ private fun setGesturesState(disabled: Boolean) {
backgroundScope.launch {
sysUiState
- .setFlag(QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING, enabled)
+ .setFlag(QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED, disabled)
.commitUpdate(displayTracker.defaultDisplayId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt
index 11740a8..9e6553a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt
@@ -18,11 +18,13 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
-class TouchpadTutorialViewModel : ViewModel() {
+class TouchpadTutorialViewModel(private val gesturesInteractor: TouchpadGesturesInteractor) :
+ ViewModel() {
private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION)
val screen: StateFlow<Screen> = _screen
@@ -31,11 +33,20 @@
_screen.value = screen
}
- class Factory @Inject constructor() : ViewModelProvider.Factory {
+ fun onOpened() {
+ gesturesInteractor.disableGestures()
+ }
+
+ fun onClosed() {
+ gesturesInteractor.enableGestures()
+ }
+
+ class Factory @Inject constructor(private val gesturesInteractor: TouchpadGesturesInteractor) :
+ ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
- return TouchpadTutorialViewModel() as T
+ return TouchpadTutorialViewModel(gesturesInteractor) as T
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/BackGestureTutorialScreen.kt
new file mode 100644
index 0000000..2460761c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/BackGestureTutorialScreen.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.touchpad.tutorial.ui.view
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.android.systemui.res.R
+
+@Composable
+fun BackGestureTutorialScreen(
+ onDoneButtonClicked: () -> Unit,
+ onBack: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BackHandler { onBack() }
+ Column(
+ verticalArrangement = Arrangement.Center,
+ modifier =
+ modifier
+ .background(color = MaterialTheme.colorScheme.surfaceContainer)
+ .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
+ .fillMaxSize()
+ ) {
+ Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
+ TutorialDescription(modifier = Modifier.weight(1f))
+ Spacer(modifier = Modifier.width(76.dp))
+ TutorialAnimation(modifier = Modifier.weight(1f).padding(top = 24.dp))
+ }
+ DoneButton(onDoneButtonClicked = onDoneButtonClicked)
+ }
+}
+
+@Composable
+fun TutorialDescription(modifier: Modifier = Modifier) {
+ Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
+ Text(
+ text = stringResource(id = R.string.touchpad_back_gesture_action_title),
+ style = MaterialTheme.typography.displayLarge
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = stringResource(id = R.string.touchpad_back_gesture_guidance),
+ style = MaterialTheme.typography.bodyLarge
+ )
+ }
+}
+
+@Composable
+fun TutorialAnimation(modifier: Modifier = Modifier) {
+ // below are just placeholder images, will be substituted by animations soon
+ Column(modifier = modifier.fillMaxWidth()) {
+ Image(
+ painter = painterResource(id = R.drawable.placeholder_touchpad_tablet_back_gesture),
+ contentDescription =
+ stringResource(
+ id = R.string.touchpad_back_gesture_screen_animation_content_description
+ ),
+ modifier = Modifier.fillMaxWidth()
+ )
+ Spacer(modifier = Modifier.height(24.dp))
+ Image(
+ painter = painterResource(id = R.drawable.placeholder_touchpad_back_gesture),
+ contentDescription =
+ stringResource(id = R.string.touchpad_back_gesture_animation_content_description),
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
index b7629c7..0c5c187 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -20,14 +20,13 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.Lifecycle.State.STARTED
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.compose.theme.PlatformTheme
-import com.android.systemui.touchpad.tutorial.ui.BackGestureTutorialViewModel
import com.android.systemui.touchpad.tutorial.ui.GestureViewModelFactory
import com.android.systemui.touchpad.tutorial.ui.HomeGestureTutorialViewModel
import com.android.systemui.touchpad.tutorial.ui.Screen.BACK_GESTURE
@@ -42,18 +41,27 @@
private val viewModelFactory: TouchpadTutorialViewModel.Factory,
) : ComponentActivity() {
+ private val vm by viewModels<TouchpadTutorialViewModel>(factoryProducer = { viewModelFactory })
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
- setContent {
- PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) }
- }
+ setContent { PlatformTheme { TouchpadTutorialScreen(vm) { finish() } } }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ vm.onOpened()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ vm.onClosed()
}
}
@Composable
-fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) {
- val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory)
+fun TouchpadTutorialScreen(vm: TouchpadTutorialViewModel, closeTutorial: () -> Unit) {
val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED)
when (activeScreen) {
TUTORIAL_SELECTION ->
@@ -63,17 +71,16 @@
onActionKeyTutorialClicked = {},
onDoneButtonClicked = closeTutorial
)
- BACK_GESTURE -> BackGestureTutorialScreen()
+ BACK_GESTURE ->
+ BackGestureTutorialScreen(
+ onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
+ onBack = { vm.goTo(TUTORIAL_SELECTION) }
+ )
HOME_GESTURE -> HomeGestureTutorialScreen()
}
}
@Composable
-fun BackGestureTutorialScreen() {
- val vm = viewModel<BackGestureTutorialViewModel>(factory = GestureViewModelFactory())
-}
-
-@Composable
fun HomeGestureTutorialScreen() {
val vm = viewModel<HomeGestureTutorialViewModel>(factory = GestureViewModelFactory())
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialComponents.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialComponents.kt
new file mode 100644
index 0000000..16fa91d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialComponents.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.view
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import com.android.systemui.res.R
+
+@Composable
+fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = modifier.fillMaxWidth()
+ ) {
+ Button(onClick = onDoneButtonClicked) {
+ Text(stringResource(R.string.touchpad_tutorial_done_button))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt
index 532eb1b..877bbe1 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt
@@ -22,7 +22,6 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
@@ -114,16 +113,3 @@
Text(text = text, style = MaterialTheme.typography.headlineLarge)
}
}
-
-@Composable
-private fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) {
- Row(
- horizontalArrangement = Arrangement.End,
- verticalAlignment = Alignment.CenterVertically,
- modifier = modifier.fillMaxWidth()
- ) {
- Button(onClick = onDoneButtonClicked) {
- Text(stringResource(R.string.touchpad_tutorial_done_button))
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
index aea739d..c5e98a1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
@@ -22,9 +22,12 @@
import android.annotation.BinderThread
import android.os.SystemProperties
import android.util.Log
+import android.view.View
import android.view.animation.DecelerateInterpolator
import com.android.app.tracing.TraceUtils.traceAsync
import com.android.internal.foldables.FoldLockSettingAvailabilityProvider
+import com.android.internal.jank.Cuj.CUJ_FOLD_ANIM
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -65,7 +68,8 @@
@Background private val applicationScope: CoroutineScope,
private val animationStatusRepository: AnimationStatusRepository,
private val controllerFactory: FullscreenLightRevealAnimationController.Factory,
- private val foldLockSettingAvailabilityProvider: FoldLockSettingAvailabilityProvider
+ private val foldLockSettingAvailabilityProvider: FoldLockSettingAvailabilityProvider,
+ private val interactionJankMonitor: InteractionJankMonitor
) : FullscreenLightRevealAnimation {
private val revealProgressValueAnimator: ValueAnimator =
@@ -87,7 +91,7 @@
controllerFactory.create(
displaySelector = { minByOrNull { it.naturalWidth } },
effectFactory = { LinearSideLightRevealEffect(it.isVerticalRotation()) },
- overlayContainerName = SURFACE_CONTAINER_NAME
+ overlayContainerName = OVERLAY_TITLE
)
controller.init()
@@ -149,13 +153,23 @@
private suspend fun waitForGoToSleep() =
traceAsync(TAG, "waitForGoToSleep()") { powerInteractor.isAsleep.filter { it }.first() }
- private suspend fun playFoldLightRevealOverlayAnimation() {
- revealProgressValueAnimator.duration = ANIMATION_DURATION
- revealProgressValueAnimator.interpolator = DecelerateInterpolator()
- revealProgressValueAnimator.addUpdateListener { animation ->
- controller.updateRevealAmount(animation.animatedFraction)
+ private suspend fun playFoldLightRevealOverlayAnimation() =
+ trackCuj(CUJ_FOLD_ANIM, controller.scrimView) {
+ revealProgressValueAnimator.duration = ANIMATION_DURATION
+ revealProgressValueAnimator.interpolator = DecelerateInterpolator()
+ revealProgressValueAnimator.addUpdateListener { animation ->
+ controller.updateRevealAmount(animation.animatedFraction)
+ }
+ revealProgressValueAnimator.startAndAwaitCompletion()
}
- revealProgressValueAnimator.startAndAwaitCompletion()
+
+ private suspend fun trackCuj(cuj: Int, view: View?, block: suspend () -> Unit) {
+ view?.let { interactionJankMonitor.begin(it, cuj) }
+ try {
+ block()
+ } finally {
+ if (view != null) interactionJankMonitor.end(cuj)
+ }
}
private suspend fun ValueAnimator.startAndAwaitCompletion(): Unit =
@@ -182,7 +196,7 @@
private companion object {
const val TAG = "FoldLightRevealOverlayAnimation"
const val WAIT_FOR_ANIMATION_TIMEOUT_MS = 2000L
- const val SURFACE_CONTAINER_NAME = "fold-overlay-container"
+ const val OVERLAY_TITLE = "fold-animation-overlay"
val ANIMATION_DURATION: Long
get() = SystemProperties.getLong("persist.fold_animation_duration", 200L)
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
index 135edfc..a921377 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
@@ -73,7 +73,7 @@
@Main private val executor: Executor,
@Assisted private val displaySelector: List<DisplayInfo>.() -> DisplayInfo?,
@Assisted private val lightRevealEffectFactory: (rotation: Int) -> LightRevealEffect,
- @Assisted private val overlayContainerName: String
+ @Assisted private val overlayTitle: String
) {
private lateinit var bgExecutor: Executor
@@ -81,7 +81,10 @@
private var currentRotation: Int = context.display.rotation
private var root: SurfaceControlViewHost? = null
- private var scrimView: LightRevealScrim? = null
+
+ /** The scrim view that is used to reveal the screen. */
+ var scrimView: LightRevealScrim? = null
+ private set
private val rotationWatcher = RotationWatcher()
private val internalDisplayInfos: List<DisplayInfo> =
@@ -153,7 +156,7 @@
val containerBuilder =
SurfaceControl.Builder(SurfaceSession())
.setContainerLayer()
- .setName(overlayContainerName)
+ .setName("FoldUnfoldAnimationContainer")
displayAreaHelper
.get()
@@ -221,7 +224,7 @@
}
format = PixelFormat.TRANSLUCENT
type = WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
- title = javaClass.simpleName
+ title = overlayTitle
layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
fitInsetsTypes = 0
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index f355dd8..666e75f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -71,7 +71,7 @@
fullscreenLightRevealAnimationControllerFactory.create(
displaySelector = { maxByOrNull { it.naturalWidth } },
effectFactory = { LinearLightRevealEffect(it.isVerticalRotation()) },
- overlayContainerName = SURFACE_CONTAINER_NAME,
+ overlayContainerName = OVERLAY_TITLE,
)
controller.init()
bgExecutor = threadFactory.buildDelayableExecutorOnHandler(unfoldProgressHandler)
@@ -194,7 +194,7 @@
private companion object {
const val TAG = "UnfoldLightRevealOverlayAnimation"
- const val SURFACE_CONTAINER_NAME = "unfold-overlay-container"
+ const val OVERLAY_TITLE = "unfold-animation-overlay"
const val UNFOLD_BLOCK_TOUCHES_UNTIL_PROGRESS = 0.8f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 1ae5614..2e7b05a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.volume.shared.VolumeLogger
import dagger.Module
import dagger.Provides
import kotlin.coroutines.CoroutineContext
@@ -58,6 +59,7 @@
contentResolver: ContentResolver,
@Background coroutineContext: CoroutineContext,
@Application coroutineScope: CoroutineScope,
+ volumeLogger: VolumeLogger,
): AudioRepository =
AudioRepositoryImpl(
intentsReceiver,
@@ -65,6 +67,7 @@
contentResolver,
coroutineContext,
coroutineScope,
+ volumeLogger,
)
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index 7f1faee..cfcd6b1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -16,15 +16,23 @@
package com.android.systemui.volume.panel.component.spatial.domain.interactor
+import android.bluetooth.BluetoothProfile
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
+import android.media.AudioManager
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LocalBluetoothProfile
+import com.android.settingslib.flags.Flags
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
+import com.android.settingslib.volume.data.repository.AudioRepository
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -33,6 +41,7 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/**
* Provides an ability to access and update spatial audio and head tracking state.
@@ -46,6 +55,8 @@
constructor(
audioOutputInteractor: AudioOutputInteractor,
private val spatializerInteractor: SpatializerInteractor,
+ private val audioRepository: AudioRepository,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
@VolumePanelScope private val coroutineScope: CoroutineScope,
) {
@@ -138,42 +149,85 @@
}
private suspend fun AudioOutputDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
- when (this) {
- is AudioOutputDevice.BuiltIn -> return builtinSpeaker
+ return when (this) {
+ is AudioOutputDevice.BuiltIn -> builtinSpeaker
is AudioOutputDevice.Bluetooth -> {
- return listOf(
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_HEADSET,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_SPEAKER,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLE_BROADCAST,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- cachedBluetoothDevice.address,
- ),
- AudioDeviceAttributes(
- AudioDeviceAttributes.ROLE_OUTPUT,
- AudioDeviceInfo.TYPE_HEARING_AID,
- cachedBluetoothDevice.address,
+ if (Flags.enableDeterminingSpatialAudioAttributesByProfile()) {
+ getAudioDeviceAttributesByBluetoothProfile(cachedBluetoothDevice)
+ } else {
+ listOf(
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_BROADCAST,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ cachedBluetoothDevice.address,
+ ),
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ cachedBluetoothDevice.address,
+ )
)
- )
- .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
+ .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
+ }
}
- else -> return null
+ else -> null
}
}
+ private suspend fun getAudioDeviceAttributesByBluetoothProfile(
+ cachedBluetoothDevice: CachedBluetoothDevice
+ ): AudioDeviceAttributes? =
+ withContext(backgroundCoroutineContext) {
+ cachedBluetoothDevice.profiles
+ .firstOrNull {
+ it.profileId in audioProfiles && it.isEnabled(cachedBluetoothDevice.device)
+ }
+ ?.let { profile: LocalBluetoothProfile ->
+ when (profile.profileId) {
+ BluetoothProfile.A2DP -> {
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
+ }
+ BluetoothProfile.LE_AUDIO -> {
+ when (
+ audioRepository.getBluetoothAudioDeviceCategory(
+ cachedBluetoothDevice.address
+ )
+ ) {
+ AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER ->
+ AudioDeviceInfo.TYPE_BLE_SPEAKER
+ else -> AudioDeviceInfo.TYPE_BLE_HEADSET
+ }
+ }
+ BluetoothProfile.HEARING_AID -> {
+ AudioDeviceInfo.TYPE_HEARING_AID
+ }
+ else -> null
+ }
+ }
+ ?.let {
+ AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ it,
+ cachedBluetoothDevice.address,
+ )
+ }
+ }
+
private companion object {
val builtinSpeaker =
AudioDeviceAttributes(
@@ -181,5 +235,7 @@
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
""
)
+ val audioProfiles =
+ setOf(BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index c18573e..521f608 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -26,6 +26,7 @@
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -51,6 +52,7 @@
private val context: Context,
private val audioVolumeInteractor: AudioVolumeInteractor,
private val uiEventLogger: UiEventLogger,
+ private val volumePanelLogger: VolumePanelLogger,
) : SliderViewModel {
private val volumeChanges = MutableStateFlow<Int?>(null)
@@ -105,6 +107,7 @@
audioVolumeInteractor.canChangeVolume(audioStream),
audioVolumeInteractor.ringerMode,
) { model, isEnabled, ringerMode ->
+ volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
model.toState(isEnabled, ringerMode)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
@@ -112,7 +115,10 @@
init {
volumeChanges
.filterNotNull()
- .onEach { audioVolumeInteractor.setVolume(audioStream, it) }
+ .onEach {
+ volumePanelLogger.onSetVolumeRequested(audioStream, it)
+ audioVolumeInteractor.setVolume(audioStream, it)
+ }
.launchIn(coroutineScope)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
new file mode 100644
index 0000000..cc513b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.shared
+
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.VolumeLog
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+
+private const val TAG = "SysUI_VolumePanel"
+
+/** Logs events related to the Volume Panel. */
+@VolumePanelScope
+class VolumePanelLogger @Inject constructor(@VolumeLog private val logBuffer: LogBuffer) {
+
+ fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = audioStream.toString()
+ int1 = volume
+ },
+ { "Set volume: stream=$str1 volume=$int1" }
+ )
+ }
+
+ fun onVolumeUpdateReceived(audioStream: AudioStream, volume: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = audioStream.toString()
+ int1 = volume
+ },
+ { "Volume update received: stream=$str1 volume=$int1" }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
new file mode 100644
index 0000000..869a82a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.shared
+
+import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.AudioStreamModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.VolumeLog
+import javax.inject.Inject
+
+private const val TAG = "SysUI_Volume"
+
+/** Logs general System UI volume events. */
+@SysUISingleton
+class VolumeLogger @Inject constructor(@VolumeLog private val logBuffer: LogBuffer) :
+ AudioRepositoryImpl.Logger {
+
+ override fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = audioStream.toString()
+ int1 = volume
+ },
+ { "Set volume: stream=$str1 volume=$int1" }
+ )
+ }
+
+ override fun onVolumeUpdateReceived(audioStream: AudioStream, model: AudioStreamModel) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = audioStream.toString()
+ int1 = model.volume
+ },
+ { "Volume update received: stream=$str1 volume=$int1" }
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt b/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt
index 2d84fba..5b3f08f 100644
--- a/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt
+++ b/packages/SystemUI/tests/src/androidx/core/animation/AnimatorTestRuleIsolationTest.kt
@@ -15,8 +15,8 @@
*/
package androidx.core.animation
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.doOnEnd
@@ -29,7 +29,7 @@
* This test class validates that two tests' animators are isolated from each other when using the
* same animator test rule. This is a test to prevent future instances of b/275602127.
*/
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
class AnimatorTestRuleIsolationTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 8858d13..48f6cc4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -28,6 +28,7 @@
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
@@ -40,6 +41,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
+import dagger.Lazy
import java.io.PrintWriter
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -51,7 +53,6 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import dagger.Lazy
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
@@ -111,6 +112,48 @@
ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
)
)
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL
+ )
+ )
+ }
+
+ @Test
+ fun onUnlockIntentLegacySettingChanged() {
+ // GIVEN no active unlock settings enabled
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+
+ // WHEN unlock on unlock intent legacy is allowed
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY))
+
+ // THEN active unlock triggers allowed on unlock_intent_legacy, unlock_intent,
+ // AND biometric fail
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
+ )
+ )
+ assertTrue(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
@@ -132,16 +175,21 @@
)
)
- // WHEN unlock on biometric failed is allowed
+ // WHEN unlock on unlock intent is allowed
secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser)
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
- // THEN active unlock triggers allowed on: biometric failure ONLY
+ // THEN active unlock triggers allowed on: unlock intent AND biometric failure
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE
)
)
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
assertTrue(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
@@ -184,6 +232,11 @@
)
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY
+ )
+ )
+ assertFalse(
+ activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT
)
)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
index 52af8fb..6ee8ffd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
@@ -18,10 +18,10 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -32,7 +32,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class AuthKeyguardMessageAreaTest extends SysuiTestCase {
private KeyguardMessageArea mKeyguardMessageArea;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
index ba46a87..049d77a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
@@ -17,9 +17,9 @@
package com.android.keyguard
import android.content.Context
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.AttributeSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -31,7 +31,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class BouncerKeyguardMessageAreaTest : SysuiTestCase() {
class FakeBouncerKeyguardMessageArea(context: Context, attrs: AttributeSet?) :
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
index 6512e70..bb2340a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
@@ -16,15 +16,15 @@
package com.android.keyguard
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class BouncerPanelExpansionCalculatorTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index 0a3225e..a8ab922 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -50,9 +50,9 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
import android.text.TextUtils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.logging.CarrierTextManagerLogger;
@@ -84,7 +84,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class CarrierTextManagerTest extends SysuiTestCase {
private static final CharSequence SEPARATOR = " \u2014 ";
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 69a6f2a..79933ee 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -16,10 +16,10 @@
package com.android.keyguard
import android.content.BroadcastReceiver
-import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewTreeObserver
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -76,7 +76,7 @@
import com.android.systemui.Flags as AConfigFlags
import org.mockito.Mockito.`when` as whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ClockEventControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index 9a95b17..347605d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -21,8 +21,8 @@
import android.os.PowerManager
import android.telecom.TelecomManager
import android.telephony.TelephonyManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.widget.LockPatternUtils
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class EmergencyButtonControllerTest : SysuiTestCase() {
@Mock lateinit var emergencyButton: EmergencyButton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index f9fe5e7..e724c60 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -28,10 +28,10 @@
import android.os.SystemClock;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.KeyEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.util.LatencyTracker;
@@ -54,7 +54,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
index 634dac1..d170e48 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -17,7 +17,7 @@
package com.android.keyguard
import android.hardware.biometrics.BiometricSourceType
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
@@ -38,7 +38,7 @@
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index cfa74f0..1d78168 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -30,9 +30,9 @@
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -47,7 +47,7 @@
import org.mockito.verification.VerificationMode;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchControllerBaseTest {
@Test
public void testInit_viewAlreadyAttached() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
index 9a1a4e2..c2c0f57 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt
@@ -16,8 +16,8 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
import kotlinx.coroutines.Dispatchers
@@ -26,7 +26,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardClockSwitchControllerWithCoroutinesTest : KeyguardClockSwitchControllerBaseTest() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e2063d2..83443be 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -30,7 +30,6 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -39,6 +38,7 @@
import android.widget.FrameLayout;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -55,7 +55,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
// Need to run on the main thread because KeyguardSliceView$Row init checks for
// the main thread before acquiring a wake lock. This class is constructed when
// the keyguard_clock_switch layout is inflated.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index cc36cfa..dd58ea7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -28,11 +28,11 @@
import static org.mockito.Mockito.when;
import android.hardware.display.DisplayManagerGlobal;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.DisplayInfo;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -49,7 +49,7 @@
import java.util.concurrent.Executor;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class KeyguardDisplayManagerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 6228ff8..bd81181 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -27,11 +27,11 @@
import static org.mockito.Mockito.when;
import android.hardware.biometrics.BiometricSourceType;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.Editable;
import android.text.TextWatcher;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -47,7 +47,7 @@
@SmallTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
@Mock
private ConfigurationController mConfigurationController;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index c566826..8b5372a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -23,11 +23,11 @@
import android.content.pm.PackageManager;
import android.os.Handler;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -47,7 +47,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
public class KeyguardSliceViewControllerTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index a0e8065..d96518a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -17,7 +17,6 @@
import android.graphics.Color;
import android.net.Uri;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
@@ -26,6 +25,7 @@
import androidx.slice.SliceSpecs;
import androidx.slice.builders.ListBuilder;
import androidx.slice.widget.RowContent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -44,7 +44,7 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class KeyguardSliceViewTest extends SysuiTestCase {
private KeyguardSliceView mKeyguardSliceView;
private Uri mSliceUri;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt
index ed61ee12..64e4996 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt
@@ -1,7 +1,7 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
@@ -10,7 +10,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class KeyguardStatusAreaViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
index 3b57d8f..c29439d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
@@ -16,8 +16,8 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.power.shared.model.ScreenPowerState
import kotlinx.coroutines.cancelChildren
@@ -29,7 +29,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class KeyguardStatusViewControllerWithCoroutinesTest : KeyguardStatusViewControllerBaseTest() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
index afd2034..16d2f02 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
@@ -1,7 +1,7 @@
package com.android.keyguard
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
import android.view.View
@@ -15,7 +15,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class KeyguardStatusViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 336183d..2e41246 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -16,8 +16,9 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.view.View
+import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -43,7 +44,7 @@
* the set of ids, which also dictact which direction to move and when, via a filter fn.
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardUnfoldTransitionTest : SysuiTestCase() {
private val kosmos = Kosmos()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index ee0cefb..075d8ae 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -108,10 +108,10 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.TextUtils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -176,7 +176,7 @@
import java.util.concurrent.atomic.AtomicInteger;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private static final String PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
index c674053..e7b42624 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -25,7 +25,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardUserSwitcherAnchorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
index 255c7d9..c1ba39e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
@@ -32,12 +32,12 @@
import android.graphics.Point;
import android.hardware.biometrics.BiometricSourceType;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
import android.view.HapticFeedbackConstants;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.biometrics.UdfpsController;
@@ -50,7 +50,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class LegacyLockIconViewControllerTest extends LegacyLockIconViewControllerBaseTest {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
index 9580139..2fd3cb0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
@@ -16,8 +16,8 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.LockIconView.ICON_LOCK
import com.android.systemui.doze.util.getBurnInOffset
@@ -33,7 +33,7 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControllerBaseTest() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
index 7c2550f..18976e1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
@@ -17,8 +17,8 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
@@ -32,7 +32,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class NumPadAnimatorTest : SysuiTestCase() {
@Mock lateinit var background: GradientDrawable
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt
index 835c9ff5..d8f2b10 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeHintingViewTest.kt
@@ -16,9 +16,9 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class PinShapeHintingViewTest : SysuiTestCase() {
lateinit var underTest: PinShapeHintingView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
index d431731..447cf65 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/PinShapeNonHintingViewTest.kt
@@ -16,9 +16,9 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class PinShapeNonHintingViewTest : SysuiTestCase() {
lateinit var underTest: PinShapeNonHintingView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
index 9f9b9a4..c7d11ef 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/SplitShadeTransitionAdapterTest.kt
@@ -16,10 +16,10 @@
package com.android.keyguard
import android.animation.Animator
-import android.testing.AndroidTestingRunner
import android.transition.TransitionValues
import android.view.View
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardStatusViewController.SplitShadeTransitionAdapter
import com.android.systemui.SysuiTestCase
@@ -32,7 +32,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SplitShadeTransitionAdapterTest : SysuiTestCase() {
@Mock private lateinit var KeyguardClockSwitchController: KeyguardClockSwitchController
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
index fb649c8..5247a89 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
@@ -19,7 +19,7 @@
import android.os.Looper
import android.platform.test.flag.junit.SetFlagsRule
import android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
@@ -43,7 +43,7 @@
import java.util.Optional
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ScreenOnCoordinatorTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt
index 2551650..0a9ce68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.dump.DumpManager
import org.junit.Assert.assertFalse
@@ -30,7 +30,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class BootCompleteCacheTest : SysuiTestCase() {
@@ -116,4 +116,4 @@
verify(bootCompleteListener, never()).onBootComplete()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
index f776a63..a23e829 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
@@ -4,8 +4,8 @@
import android.graphics.Rect
import android.graphics.RectF
import android.hardware.camera2.CameraManager
-import android.testing.AndroidTestingRunner
import android.util.PathParser
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
@@ -19,7 +19,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class CameraAvailabilityListenerTest : SysuiTestCase() {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 2e04007..0068644 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -18,14 +18,17 @@
import android.os.Looper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.concurrent.ExecutionException;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class DependencyTest extends SysuiTestCase {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
index 5886206..091072f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
@@ -23,11 +23,11 @@
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.Region
-import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.DisplayCutout
import android.view.DisplayInfo
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.internal.R
@@ -42,7 +42,7 @@
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DisplayCutoutBaseViewTest : SysuiTestCase() {
@@ -183,4 +183,4 @@
return@then true
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
index 46936d6..bc12aaa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/FaceScanningProviderFactoryTest.kt
@@ -16,11 +16,11 @@
import android.graphics.Point
import android.hardware.display.DisplayManagerGlobal
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.DisplayAdjustments
import android.view.DisplayInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.keyguard.KeyguardUpdateMonitor
@@ -42,7 +42,7 @@
import org.mockito.MockitoAnnotations
@RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FaceScanningProviderFactoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
index d500b5a..fb60ba3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
@@ -22,11 +22,11 @@
import android.graphics.Rect
import android.graphics.RectF
import android.hardware.graphics.common.DisplayDecorationSupport
-import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.DisplayCutout
import android.view.DisplayInfo
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.util.mockito.eq
@@ -39,7 +39,7 @@
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ScreenDecorHwcLayerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 54a14a2..997f8a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -61,7 +61,6 @@
import android.hardware.display.DisplayManager;
import android.hardware.graphics.common.DisplayDecorationSupport;
import android.os.Handler;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.PathParser;
@@ -78,6 +77,7 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -121,7 +121,7 @@
import java.util.List;
@RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class ScreenDecorationsTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 361a945..16b9ab5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.accessibility;
-import static com.android.systemui.accessibility.Magnification.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
+import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -65,7 +65,7 @@
/**
* Tests for {@link android.view.accessibility.IMagnificationConnection} retrieved from
- * {@link Magnification}
+ * {@link MagnificationImpl}
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -104,7 +104,7 @@
private IWindowManager mIWindowManager;
private IMagnificationConnection mIMagnificationConnection;
- private Magnification mMagnification;
+ private MagnificationImpl mMagnification;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private TestableLooper mTestableLooper;
@@ -119,11 +119,11 @@
any(IMagnificationConnection.class));
mTestableLooper = TestableLooper.get(this);
assertNotNull(mTestableLooper);
- mMagnification = new Magnification(getContext(),
+ mMagnification = new MagnificationImpl(getContext(),
mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
mDisplayTracker, getContext().getSystemService(DisplayManager.class),
- mA11yLogger, mIWindowManager);
+ mA11yLogger, mIWindowManager, mAccessibilityManager);
mMagnification.mWindowMagnificationControllerSupplier =
new FakeWindowMagnificationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index 17b7e21..41a4116 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -84,7 +84,7 @@
private SecureSettings mSecureSettings;
private CommandQueue mCommandQueue;
- private Magnification mMagnification;
+ private MagnificationImpl mMagnification;
private OverviewProxyListener mOverviewProxyListener;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
@@ -124,11 +124,12 @@
when(mWindowMagnificationController.isActivated()).thenReturn(true);
mCommandQueue = new CommandQueue(getContext(), mDisplayTracker);
- mMagnification = new Magnification(getContext(),
+ mMagnification = new MagnificationImpl(getContext(),
getContext().getMainThreadHandler(), mContext.getMainExecutor(),
mCommandQueue, mModeSwitchesController,
mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
- getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager);
+ getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager,
+ getContext().getSystemService(AccessibilityManager.class));
mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
index f46b2f9..53b98d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -26,10 +26,14 @@
import android.hardware.input.InputManager;
import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.testing.TestableLooper;
import android.view.KeyEvent;
+import android.view.accessibility.Flags;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -44,6 +48,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -77,6 +82,9 @@
private SystemActions mSystemActions;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
@@ -131,4 +139,40 @@
verify(mTelecomManager).endCall();
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_GLOBAL_ACTION_MENU)
+ public void handleMenu_injectsKeyEvents() {
+ final List<KeyEvent> keyEvents = new ArrayList<>();
+ doAnswer(invocation -> {
+ keyEvents.add(new KeyEvent(invocation.getArgument(0)));
+ return null;
+ }).when(mInputManager).injectInputEvent(any(), anyInt());
+
+ mSystemActions.handleMenu();
+
+ assertThat(keyEvents.size()).isEqualTo(2);
+ assertThat(keyEvents.get(0).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MENU);
+ assertThat(keyEvents.get(0).getAction()).isEqualTo(KeyEvent.ACTION_DOWN);
+ assertThat(keyEvents.get(1).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MENU);
+ assertThat(keyEvents.get(1).getAction()).isEqualTo(KeyEvent.ACTION_UP);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_GLOBAL_ACTION_MEDIA_PLAY_PAUSE)
+ public void handleMediaPlayPause_injectsKeyEvents() {
+ final List<KeyEvent> keyEvents = new ArrayList<>();
+ doAnswer(invocation -> {
+ keyEvents.add(new KeyEvent(invocation.getArgument(0)));
+ return null;
+ }).when(mInputManager).injectInputEvent(any(), anyInt());
+
+ mSystemActions.handleMediaPlayPause();
+
+ assertThat(keyEvents.size()).isEqualTo(2);
+ assertThat(keyEvents.get(0).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ assertThat(keyEvents.get(0).getAction()).isEqualTo(KeyEvent.ACTION_DOWN);
+ assertThat(keyEvents.get(1).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ assertThat(keyEvents.get(1).getAction()).isEqualTo(KeyEvent.ACTION_UP);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 38095c8..c30bedd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -258,8 +258,9 @@
setupEnabledAccessibilityServiceList();
mMenuViewLayer.mDismissMenuAction.run();
- final String value = Settings.Secure.getString(mSpyContext.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ final String value = Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT));
assertThat(value).isEqualTo("");
}
@@ -274,8 +275,9 @@
ShortcutConstants.UserShortcutType.HARDWARE)).thenReturn(stubShortcutTargets);
mMenuViewLayer.mDismissMenuAction.run();
- final String value = Settings.Secure.getString(mSpyContext.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ final String value = Settings.Secure.getStringForUser(mSpyContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT));
assertThat(value).isEqualTo(TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
}
@@ -445,9 +447,11 @@
}
private void setupEnabledAccessibilityServiceList() {
- Settings.Secure.putString(mSpyContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mSpyContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString());
+ TEST_SELECT_TO_SPEAK_COMPONENT_NAME.flattenToString(),
+ mSecureSettings.getRealUserHandle(UserHandle.USER_CURRENT)
+ );
final ResolveInfo resolveInfo = new ResolveInfo();
final ServiceInfo serviceInfo = new ServiceInfo();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
index 51f6cdb..7320bbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
@@ -22,9 +22,9 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -44,7 +44,7 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HearingDevicesCheckerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index 4118c90..64c162f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -212,6 +212,40 @@
}
@Test
+ public void testSessionResetOnLifecycle() {
+ final TouchHandler touchHandler = createTouchHandler();
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[1];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any(), any(), any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)), mKosmos);
+
+ // Ensure touch outside specified region is not delivered.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+
+ // Make sure touch inside region causes session start.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler).onSessionStart(any());
+
+ Mockito.clearInvocations(touchHandler);
+
+ // Reset lifecycle, forcing monitoring to be reset
+ environment.updateLifecycle(Lifecycle.State.STARTED);
+ environment.updateLifecycle(Lifecycle.State.RESUMED);
+ environment.executeAll();
+
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler).onSessionStart(any());
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_AMBIENT_TOUCH_MONITOR_LISTEN_TO_DISPLAY_CHANGES)
public void testConfigurationListenerUpdatesBounds() {
final TouchHandler touchHandler = createTouchHandler();
@@ -332,7 +366,6 @@
}
}
-
@Test
public void testNoActiveSessionWhenHandlerDisabled() {
final TouchHandler touchHandler = Mockito.mock(TouchHandler.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
index 982f033..99d3600 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.battery
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT
@@ -24,8 +25,10 @@
import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH_WITH_SHIELD
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class AccessorizedBatteryDrawableTest : SysuiTestCase() {
@Test
fun intrinsicSize_shieldFalse_isBatterySize() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
index 14eff2f..d940fc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
@@ -31,6 +31,7 @@
import android.os.Handler;
import android.provider.Settings;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -44,10 +45,12 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class BatteryMeterViewControllerTest extends SysuiTestCase {
@Mock
private BatteryMeterView mBatteryMeterView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt
index 39cb047..cac0b66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.battery
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatterySpecs.BATTERY_HEIGHT
@@ -24,8 +25,10 @@
import com.android.systemui.battery.BatterySpecs.BATTERY_WIDTH_WITH_SHIELD
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class BatterySpecsTest : SysuiTestCase() {
@Test
fun getFullBatteryHeight_shieldFalse_returnsMainHeight() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 4238254..7fa165c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -73,6 +73,7 @@
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.argumentCaptor
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -218,6 +219,13 @@
verify(kosmos.windowManager).addView(any(), any())
+ var viewCaptor = argumentCaptor<View>()
+ verify(kosmos.windowManager).addView(viewCaptor.capture(), any())
+ verify(viewCaptor.firstValue)
+ .announceForAccessibility(
+ mContext.getText(R.string.accessibility_side_fingerprint_indicator_label)
+ )
+
// Hide alternate bouncer
kosmos.keyguardBouncerRepository.setAlternateVisible(false)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 93c6d9e..97f5efc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -17,11 +17,11 @@
package com.android.systemui.biometrics.ui.viewmodel
import android.app.ActivityManager.RunningTaskInfo
-import android.app.ActivityTaskManager
import android.content.ComponentName
+import android.content.applicationContext
+import android.content.packageManager
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Configuration
import android.graphics.Bitmap
@@ -41,26 +41,20 @@
import android.platform.test.annotations.EnableFlags
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import android.view.Surface
import androidx.test.filters.SmallTest
-import com.android.internal.widget.LockPatternUtils
-import com.android.launcher3.icons.IconProvider
+import com.android.app.activityTaskManager
import com.android.systemui.Flags.FLAG_BP_TALKBACK
import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils.toBitmap
-import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
-import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.biometrics.data.repository.FakePromptRepository
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
-import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.authController
+import com.android.systemui.biometrics.data.repository.biometricStatusRepository
+import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
-import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
+import com.android.systemui.biometrics.domain.interactor.promptSelectorInteractor
+import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
@@ -68,18 +62,20 @@
import com.android.systemui.biometrics.shared.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.biometrics.shared.model.toSensorStrength
import com.android.systemui.biometrics.shared.model.toSensorType
+import com.android.systemui.biometrics.udfpsUtils
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -94,7 +90,9 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -113,20 +111,12 @@
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- @Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var authController: AuthController
- @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
- @Mock private lateinit var udfpsUtils: UdfpsUtils
- @Mock private lateinit var packageManager: PackageManager
- @Mock private lateinit var iconProvider: IconProvider
@Mock private lateinit var applicationInfoWithIcon: ApplicationInfo
@Mock private lateinit var applicationInfoNoIcon: ApplicationInfo
- @Mock private lateinit var activityTaskManager: ActivityTaskManager
@Mock private lateinit var activityInfo: ActivityInfo
@Mock private lateinit var runningTaskInfo: RunningTaskInfo
- private val fakeExecutor = FakeExecutor(FakeSystemClock())
- private val testScope = TestScope()
private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
private val logoResFromApp = R.drawable.ic_cake
@@ -156,28 +146,81 @@
context.resources.getDimensionPixelSize(
R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding
)
+ private val mockFaceIconSize = 200
+ private val mockFingerprintIconWidth = 300
+ private val mockFingerprintIconHeight = 300
- private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
- private lateinit var promptRepository: FakePromptRepository
- private lateinit var displayStateRepository: FakeDisplayStateRepository
- private lateinit var biometricStatusRepository: FakeBiometricStatusRepository
- private lateinit var displayRepository: FakeDisplayRepository
- private lateinit var displayStateInteractor: DisplayStateInteractor
- private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
- private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+ /** Mock [UdfpsOverlayParams] for a test. */
+ private fun mockUdfpsOverlayParams(isLandscape: Boolean = false): UdfpsOverlayParams =
+ UdfpsOverlayParams(
+ sensorBounds = Rect(400, 1600, 600, 1800),
+ overlayBounds = Rect(0, 1200, 1000, 2400),
+ naturalDisplayWidth = 1000,
+ naturalDisplayHeight = 3000,
+ scaleFactor = 1f,
+ rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0
+ )
- private lateinit var selector: PromptSelectorInteractor
- private lateinit var viewModel: PromptViewModel
- private lateinit var iconViewModel: PromptIconViewModel
private lateinit var promptContentView: PromptContentView
private lateinit var promptContentViewWithMoreOptionsButton:
PromptContentViewWithMoreOptionsButton
+ private val kosmos = Kosmos()
+
@Before
fun setup() {
- fingerprintRepository = FakeFingerprintPropertyRepository()
+
+ // Set up default logo info and app customized info
+ whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
+ .thenReturn(applicationInfoNoIcon)
+ whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
+ .thenReturn(applicationInfoWithIcon)
+ whenever(
+ kosmos.packageManager.getApplicationInfo(
+ eq(packageNameForLogoWithOverrides),
+ anyInt()
+ )
+ )
+ .thenReturn(applicationInfoWithIcon)
+ whenever(
+ kosmos.packageManager.getApplicationInfo(
+ eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND),
+ anyInt()
+ )
+ )
+ .thenThrow(NameNotFoundException())
+
+ whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
+ whenever(kosmos.iconProvider.getIcon(activityInfo)).thenReturn(defaultLogoIconWithOverrides)
+ whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIcon))
+ .thenReturn(defaultLogoIcon)
+ whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIcon))
+ .thenReturn(defaultLogoDescription)
+ whenever(kosmos.packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
+ whenever(kosmos.packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
+
+ context.setMockPackageManager(kosmos.packageManager)
+ overrideResource(logoResFromApp, logoDrawableFromAppRes)
+ overrideResource(
+ R.array.biometric_dialog_package_names_for_logo_with_overrides,
+ arrayOf(packageNameForLogoWithOverrides)
+ )
+
+ overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth)
+ overrideResource(
+ R.dimen.biometric_dialog_fingerprint_icon_height,
+ mockFingerprintIconHeight
+ )
+ overrideResource(R.dimen.biometric_dialog_face_icon_size, mockFaceIconSize)
+
+ kosmos.applicationContext = context
+
+ if (testCase.fingerprint?.isAnyUdfpsType == true) {
+ kosmos.authController = authController
+ }
+
testCase.fingerprint?.let {
- fingerprintRepository.setProperties(
+ kosmos.fakeFingerprintPropertyRepository.setProperties(
it.sensorId,
it.sensorStrength.toSensorStrength(),
it.sensorType.toSensorType(),
@@ -186,31 +229,20 @@
}
)
}
- promptRepository = FakePromptRepository()
- displayStateRepository = FakeDisplayStateRepository()
- displayRepository = FakeDisplayRepository()
- displayStateInteractor =
- DisplayStateInteractorImpl(
- testScope.backgroundScope,
- mContext,
- fakeExecutor,
- displayStateRepository,
- displayRepository,
- )
- udfpsOverlayInteractor =
- UdfpsOverlayInteractor(
- context,
- authController,
- selectedUserInteractor,
- testScope.backgroundScope
- )
- biometricStatusRepository = FakeBiometricStatusRepository()
- biometricStatusInteractor =
- BiometricStatusInteractorImpl(
- activityTaskManager,
- biometricStatusRepository,
- fingerprintRepository
- )
+
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ testCase.fingerprint?.isAnySidefpsType.let {
+ kosmos.displayStateRepository.setIsInRearDisplayMode(testCase.isInRearDisplayMode)
+ if (testCase.isDeviceFolded) {
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(
+ getFoldedConfiguration()
+ )
+ } else {
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(
+ getUnfoldedConfiguration()
+ )
+ }
+ }
promptContentView =
PromptVerticalListContentView.Builder()
@@ -221,47 +253,29 @@
promptContentViewWithMoreOptionsButton =
PromptContentViewWithMoreOptionsButton.Builder()
.setDescription("test")
- .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
+ .setMoreOptionsButtonListener(kosmos.fakeExecutor) { _, _ -> }
.build()
-
- // Set up default logo info and app customized info
- whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt()))
- .thenReturn(applicationInfoNoIcon)
- whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME), anyInt()))
- .thenReturn(applicationInfoWithIcon)
- whenever(packageManager.getApplicationInfo(eq(packageNameForLogoWithOverrides), anyInt()))
- .thenReturn(applicationInfoWithIcon)
- whenever(packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND), anyInt()))
- .thenThrow(NameNotFoundException())
-
- whenever(packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo)
- whenever(iconProvider.getIcon(activityInfo)).thenReturn(defaultLogoIconWithOverrides)
- whenever(packageManager.getApplicationIcon(applicationInfoWithIcon))
- .thenReturn(defaultLogoIcon)
- whenever(packageManager.getApplicationLabel(applicationInfoWithIcon))
- .thenReturn(defaultLogoDescription)
- whenever(packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) }
- whenever(packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) }
-
- context.setMockPackageManager(packageManager)
- val resources = context.getOrCreateTestableResources()
- resources.addOverride(logoResFromApp, logoDrawableFromAppRes)
- resources.addOverride(
- R.array.biometric_dialog_package_names_for_logo_with_overrides,
- arrayOf(packageNameForLogoWithOverrides)
- )
}
@Test
fun start_idle_and_show_authenticating() =
runGenericTest(doNotStart = true) {
- val expectedSize =
+ var expectedPromptSize =
if (testCase.shouldStartAsImplicitFlow) PromptSize.SMALL else PromptSize.MEDIUM
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val modalities by collectLastValue(viewModel.modalities)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val modalities by collectLastValue(kosmos.promptViewModel.modalities)
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isNotAuthenticated).isTrue()
@@ -269,131 +283,49 @@
assertThat(hasFace).isEqualTo(testCase.face != null)
assertThat(hasFingerprint).isEqualTo(testCase.fingerprint != null)
}
+
assertThat(message).isEqualTo(PromptMessage.Empty)
- assertThat(size).isEqualTo(expectedSize)
+ assertThat(size).isEqualTo(expectedPromptSize)
- val startMessage = "here we go"
- viewModel.showAuthenticating(startMessage, isRetry = false)
+ val forceExplicitFlow =
+ testCase.isCoex && testCase.confirmationRequested ||
+ testCase.authenticatedByFingerprint
- assertThat(message).isEqualTo(PromptMessage.Help(startMessage))
- assertThat(authenticating).isTrue()
- assertThat(authenticated?.isNotAuthenticated).isTrue()
- assertThat(size).isEqualTo(expectedSize)
- assertButtonsVisible(negative = expectedSize != PromptSize.SMALL)
- }
-
- @Test
- fun shows_authenticated_with_no_errors() = runGenericTest {
- // this case can't happen until fingerprint is started
- // trigger it now since no error has occurred in this test
- val forceError = testCase.isCoex && testCase.authenticatedByFingerprint
-
- if (forceError) {
- assertThat(viewModel.fingerprintStartMode.first())
- .isEqualTo(FingerprintStartMode.Pending)
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
- }
-
- showAuthenticated(
- testCase.authenticatedModality,
- testCase.expectConfirmation(atLeastOneFailure = forceError),
- )
- }
-
- @Test
- fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
- runGenericTest {
- val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
-
- viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
-
- val confirmHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmHaptics?.hapticFeedbackConstant)
- .isEqualTo(
- if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
- else HapticFeedbackConstants.CONFIRM
- )
- assertThat(confirmHaptics?.flag)
- .isEqualTo(
- if (expectConfirmation) null
- else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
- )
-
- if (expectConfirmation) {
- viewModel.confirmAuthenticated()
+ if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) {
+ // Face-only or implicit co-ex auth
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_idle_static)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(shouldAnimateIconView).isEqualTo(false)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
- val confirmedHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmedHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(confirmedHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
- }
-
- @Test
- fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
- val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
-
- if (expectConfirmation) {
- viewModel.confirmAuthenticated()
- }
-
- val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
- }
-
- @Test
- fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
- viewModel.showTemporaryError("test", "messageAfterError", false)
-
- val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
- assertThat(currentHaptics?.flag)
- .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
- }
-
- @Test
- fun start_idle_and_show_authenticating_iconUpdate() =
- runGenericTest(doNotStart = true) {
- val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- val forceExplicitFlow = testCase.isCoex && testCase.authenticatedByFingerprint
if (forceExplicitFlow) {
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ expectedPromptSize = PromptSize.MEDIUM
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
}
val startMessage = "here we go"
- viewModel.showAuthenticating(startMessage, isRetry = false)
+ kosmos.promptViewModel.showAuthenticating(startMessage, isRetry = false)
+ verifyIconSize(forceExplicitFlow)
- if (testCase.isFingerprintOnly) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
+ // Icon asset assertions
+ if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) {
+ // Face-only or implicit co-ex auth
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_authenticating)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
+ // Fingerprint-only or explicit co-ex auth
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw
- .biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_fingerprint_to_error_landscape)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(false)
} else {
assertThat(iconAsset)
@@ -406,85 +338,74 @@
}
}
- if (testCase.isFaceOnly) {
- val expectedIconAsset = R.raw.face_dialog_authenticating
- assertThat(iconAsset).isEqualTo(expectedIconAsset)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- }
-
- if (testCase.isCoex) {
- if (testCase.confirmationRequested || forceExplicitFlow) {
- // explicit flow
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
- // TODO: Update when SFPS co-ex is implemented
- if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- assertThat(iconAsset)
- .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
- assertThat(iconOverlayAsset).isEqualTo(-1)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
- assertThat(shouldAnimateIconView).isEqualTo(false)
- assertThat(shouldAnimateIconOverlay).isEqualTo(false)
- }
- } else {
- // implicit flow
- val expectedIconAsset = R.raw.face_dialog_authenticating
- assertThat(iconAsset).isEqualTo(expectedIconAsset)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- }
- }
+ assertThat(message).isEqualTo(PromptMessage.Help(startMessage))
+ assertThat(authenticating).isTrue()
+ assertThat(authenticated?.isNotAuthenticated).isTrue()
+ assertThat(size).isEqualTo(expectedPromptSize)
+ assertButtonsVisible(negative = expectedPromptSize != PromptSize.SMALL)
}
@Test
- fun start_authenticating_show_and_clear_error_iconUpdate() = runGenericTest {
- val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
+ fun start_authenticating_show_and_clear_error() = runGenericTest {
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ val message by collectLastValue(kosmos.promptViewModel.message)
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- val forceExplicitFlow = testCase.isCoex && testCase.authenticatedByFingerprint
+ var forceExplicitFlow =
+ testCase.isCoex && testCase.confirmationRequested || testCase.authenticatedByFingerprint
if (forceExplicitFlow) {
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
}
+ verifyIconSize(forceExplicitFlow)
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"so sad",
messageAfterError = "",
authenticateAfterError = testCase.isFingerprintOnly || testCase.isCoex,
)
+ forceExplicitFlow = true
// Usually done by binder
- iconViewModel.setPreviousIconWasError(true)
- iconViewModel.setPreviousIconOverlayWasError(true)
+ kosmos.promptViewModel.iconViewModel.setPreviousIconWasError(true)
+ kosmos.promptViewModel.iconViewModel.setPreviousIconOverlayWasError(true)
}
- if (testCase.isFingerprintOnly) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by collectLastValue(iconViewModel.shouldAnimateIconOverlay)
+ assertThat(message?.isError).isEqualTo(true)
+ assertThat(message?.message).isEqualTo("so sad")
+ // Icon asset assertions
+ if (testCase.isFaceOnly) {
+ // Face-only auth
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_error)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+
+ // Clear error, go to idle
+ errorJob.join()
+
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_error_to_idle)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
+ assertThat(iconContentDescriptionId)
+ .isEqualTo(R.string.biometric_dialog_face_icon_description_idle)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
+ // Fingerprint-only or explicit co-ex auth
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_fingerprint_to_error_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_fingerprint_to_error_landscape)
assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(true)
} else {
assertThat(iconAsset)
@@ -499,19 +420,9 @@
errorJob.join()
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_symbol_error_to_fingerprint_landscape)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
assertThat(shouldAnimateIconOverlay).isEqualTo(true)
@@ -525,89 +436,48 @@
assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
}
+ }
- if (testCase.isFaceOnly) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_error)
- assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed)
- assertThat(shouldAnimateIconView).isEqualTo(true)
-
- // Clear error, go to idle
- errorJob.join()
-
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_error_to_idle)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.biometric_dialog_face_icon_description_idle)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- }
-
- if (testCase.isCoex) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
- // TODO: Update when SFPS co-ex is implemented
- if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- assertThat(iconAsset)
- .isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_error_lottie)
- assertThat(iconOverlayAsset).isEqualTo(-1)
- assertThat(iconContentDescriptionId).isEqualTo(R.string.biometric_dialog_try_again)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- assertThat(shouldAnimateIconOverlay).isEqualTo(false)
- }
-
- // Clear error, restart authenticating
- errorJob.join()
-
- if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- assertThat(iconAsset)
- .isEqualTo(R.raw.fingerprint_dialogue_error_to_fingerprint_lottie)
- assertThat(iconOverlayAsset).isEqualTo(-1)
- assertThat(iconContentDescriptionId)
- .isEqualTo(R.string.fingerprint_dialog_touch_sensor)
- assertThat(shouldAnimateIconView).isEqualTo(true)
- assertThat(shouldAnimateIconOverlay).isEqualTo(false)
- }
+ private fun getSfpsBaseIconAsset(): Int {
+ return if (testCase.isInRearDisplayMode) {
+ R.raw.biometricprompt_rear_landscape_base
+ } else if (testCase.isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_default
+ } else {
+ R.raw.biometricprompt_landscape_base
}
}
@Test
- fun shows_authenticated_no_errors_no_confirmation_required_iconUpdate() = runGenericTest {
+ fun shows_authenticated_no_errors_no_confirmation_required() = runGenericTest {
if (!testCase.confirmationRequested) {
- val currentRotation by collectLastValue(displayStateInteractor.currentRotation)
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ verifyIconSize()
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
-
- viewModel.showAuthenticated(
+ kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
dismissAfterDelay = DELAY
)
if (testCase.isFingerprintOnly) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
+ // Fingerprint icon asset assertions
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val expectedOverlayAsset =
- when (currentRotation) {
- DisplayRotation.ROTATION_0 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
- DisplayRotation.ROTATION_90 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
- DisplayRotation.ROTATION_180 ->
- R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
- DisplayRotation.ROTATION_270 ->
- R.raw
- .biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
- else -> throw Exception("invalid rotation")
- }
- assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
+ assertThat(iconAsset).isEqualTo(getSfpsBaseIconAsset())
+ assertThat(iconOverlayAsset)
+ .isEqualTo(R.raw.biometricprompt_symbol_fingerprint_to_success_landscape)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.security_settings_sfps_enroll_find_sensor_message)
+ assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(true)
} else {
- val isAuthenticated by collectLastValue(viewModel.isAuthenticated)
assertThat(iconAsset)
.isEqualTo(R.raw.fingerprint_dialogue_fingerprint_to_success_lottie)
assertThat(iconOverlayAsset).isEqualTo(-1)
@@ -616,47 +486,52 @@
assertThat(shouldAnimateIconView).isEqualTo(true)
assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
- }
-
- // If co-ex, using implicit flow (explicit flow always requires confirmation)
- if (testCase.isFaceOnly || testCase.isCoex) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
+ } else if (testCase.isFaceOnly || testCase.isCoex) {
+ // Face icon asset assertions
+ // If co-ex, use implicit flow (explicit flow always requires confirmation)
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
}
}
@Test
- fun shows_pending_confirmation_iconUpdate() = runGenericTest {
+ fun shows_pending_confirmation() = runGenericTest {
if (
(testCase.isFaceOnly || testCase.isCoex) &&
testCase.authenticatedByFace &&
testCase.confirmationRequested
) {
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
- viewModel.showAuthenticated(
+ val forceExplicitFlow = testCase.isCoex && testCase.confirmationRequested
+ verifyIconSize(forceExplicitFlow = forceExplicitFlow)
+
+ kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
dismissAfterDelay = DELAY
)
if (testCase.isFaceOnly) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_wink_from_dark)
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_wink_from_dark)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
assertThat(shouldAnimateIconView).isEqualTo(true)
- }
-
- // explicit flow because confirmation requested
- if (testCase.isCoex) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
+ } else if (testCase.isCoex) { // explicit flow, confirmation requested
// TODO: Update when SFPS co-ex is implemented
if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
assertThat(iconAsset)
@@ -678,30 +553,36 @@
testCase.authenticatedByFace &&
testCase.confirmationRequested
) {
- val iconAsset by collectLastValue(iconViewModel.iconAsset)
- val iconContentDescriptionId by collectLastValue(iconViewModel.contentDescriptionId)
- val shouldAnimateIconView by collectLastValue(iconViewModel.shouldAnimateIconView)
+ val iconAsset by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+ val iconOverlayAsset by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.iconOverlayAsset)
+ val iconContentDescriptionId by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.contentDescriptionId)
+ val shouldAnimateIconView by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconView)
+ val shouldAnimateIconOverlay by
+ collectLastValue(kosmos.promptViewModel.iconViewModel.shouldAnimateIconOverlay)
+ val forceExplicitFlow = testCase.isCoex && testCase.confirmationRequested
+ verifyIconSize(forceExplicitFlow = forceExplicitFlow)
- viewModel.showAuthenticated(
+ kosmos.promptViewModel.showAuthenticated(
modality = testCase.authenticatedModality,
dismissAfterDelay = DELAY
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
if (testCase.isFaceOnly) {
- assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
+ assertThat(iconAsset).isEqualTo(R.raw.face_dialog_dark_to_checkmark)
+ assertThat(iconOverlayAsset).isEqualTo(-1)
assertThat(iconContentDescriptionId)
.isEqualTo(R.string.biometric_dialog_face_icon_description_confirmed)
assertThat(shouldAnimateIconView).isEqualTo(true)
+ assertThat(shouldAnimateIconOverlay).isEqualTo(false)
}
// explicit flow because confirmation requested
if (testCase.isCoex) {
- val iconOverlayAsset by collectLastValue(iconViewModel.iconOverlayAsset)
- val shouldAnimateIconOverlay by
- collectLastValue(iconViewModel.shouldAnimateIconOverlay)
-
// TODO: Update when SFPS co-ex is implemented
if (testCase.sensorType != FingerprintSensorProperties.TYPE_POWER_BUTTON) {
assertThat(iconAsset)
@@ -717,19 +598,119 @@
}
@Test
- fun sfpsIconUpdates_onConfigurationChanged() = runGenericTest {
- if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val testConfig = Configuration()
- val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
- val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
- val currentIcon by collectLastValue(iconViewModel.iconAsset)
+ fun shows_authenticated_with_no_errors() = runGenericTest {
+ // this case can't happen until fingerprint is started
+ // trigger it now since no error has occurred in this test
+ val forceError = testCase.isCoex && testCase.authenticatedByFingerprint
- testConfig.smallestScreenWidthDp = folded
- iconViewModel.onConfigurationChanged(testConfig)
+ if (forceError) {
+ assertThat(kosmos.promptViewModel.fingerprintStartMode.first())
+ .isEqualTo(FingerprintStartMode.Pending)
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
+ }
+
+ showAuthenticated(
+ testCase.authenticatedModality,
+ testCase.expectConfirmation(atLeastOneFailure = forceError),
+ )
+ }
+
+ // Verifies expected icon sizes for all modalities
+ private fun TestScope.verifyIconSize(forceExplicitFlow: Boolean = false) {
+ val iconSize by collectLastValue(kosmos.promptViewModel.iconSize)
+ if ((testCase.isCoex && !forceExplicitFlow) || testCase.isFaceOnly) {
+ // Face-only or implicit co-ex auth
+ assertThat(iconSize).isEqualTo(Pair(mockFaceIconSize, mockFaceIconSize))
+ } else if ((testCase.isCoex && forceExplicitFlow) || testCase.isFingerprintOnly) {
+ // Fingerprint-only or explicit co-ex auth
+ if (testCase.fingerprint?.isAnyUdfpsType == true) {
+ val udfpsOverlayParams by
+ collectLastValue(kosmos.promptViewModel.udfpsOverlayParams)
+ val expectedUdfpsOverlayParams = mockUdfpsOverlayParams()
+ assertThat(udfpsOverlayParams).isEqualTo(expectedUdfpsOverlayParams)
+
+ assertThat(iconSize)
+ .isEqualTo(
+ Pair(
+ expectedUdfpsOverlayParams.sensorBounds.width(),
+ expectedUdfpsOverlayParams.sensorBounds.height()
+ )
+ )
+ } else {
+ assertThat(iconSize)
+ .isEqualTo(Pair(mockFingerprintIconWidth, mockFingerprintIconHeight))
+ }
+ }
+ }
+
+ @Test
+ fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
+ runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+
+ val confirmHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(confirmHaptics?.hapticFeedbackConstant)
+ .isEqualTo(
+ if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
+ else HapticFeedbackConstants.CONFIRM
+ )
+ assertThat(confirmHaptics?.flag)
+ .isEqualTo(
+ if (expectConfirmation) null
+ else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
+ )
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val confirmedHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(confirmedHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(confirmedHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ }
+
+ @Test
+ fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ }
+
+ @Test
+ fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
+
+ val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+ }
+
+ @Test
+ fun sfpsIconUpdates_onFoldConfigurationChanged() = runGenericTest {
+ if (
+ testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON &&
+ !testCase.isInRearDisplayMode
+ ) {
+ val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
+
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(getFoldedConfiguration())
val foldedIcon = currentIcon
- testConfig.smallestScreenWidthDp = unfolded
- iconViewModel.onConfigurationChanged(testConfig)
+ kosmos.promptViewModel.iconViewModel.onConfigurationChanged(getUnfoldedConfiguration())
val unfoldedIcon = currentIcon
assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
@@ -739,18 +720,18 @@
@Test
fun sfpsIconUpdates_onRotation() = runGenericTest {
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val currentIcon by collectLastValue(iconViewModel.iconAsset)
+ val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
val iconRotation0 = currentIcon
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
val iconRotation90 = currentIcon
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
val iconRotation180 = currentIcon
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
val iconRotation270 = currentIcon
assertThat(iconRotation0).isEqualTo(iconRotation180)
@@ -762,12 +743,12 @@
@Test
fun sfpsIconUpdates_onRearDisplayMode() = runGenericTest {
if (testCase.sensorType == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
- val currentIcon by collectLastValue(iconViewModel.iconAsset)
+ val currentIcon by collectLastValue(kosmos.promptViewModel.iconViewModel.iconAsset)
- displayStateRepository.setIsInRearDisplayMode(false)
+ kosmos.displayStateRepository.setIsInRearDisplayMode(false)
val iconNotRearDisplayMode = currentIcon
- displayStateRepository.setIsInRearDisplayMode(true)
+ kosmos.displayStateRepository.setIsInRearDisplayMode(true)
val iconRearDisplayMode = currentIcon
assertThat(iconNotRearDisplayMode).isNotEqualTo(iconRearDisplayMode)
@@ -778,10 +759,10 @@
authenticatedModality: BiometricModality,
expectConfirmation: Boolean,
) {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val fpStartMode by collectLastValue(viewModel.fingerprintStartMode)
- val size by collectLastValue(viewModel.size)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val fpStartMode by collectLastValue(kosmos.promptViewModel.fingerprintStartMode)
+ val size by collectLastValue(kosmos.promptViewModel.size)
val authWithSmallPrompt =
testCase.shouldStartAsImplicitFlow &&
@@ -791,7 +772,7 @@
assertThat(size).isEqualTo(if (authWithSmallPrompt) PromptSize.SMALL else PromptSize.MEDIUM)
assertButtonsVisible(negative = !authWithSmallPrompt)
- viewModel.showAuthenticated(authenticatedModality, DELAY)
+ kosmos.promptViewModel.showAuthenticated(authenticatedModality, DELAY)
assertThat(authenticated?.isAuthenticated).isTrue()
assertThat(authenticated?.delay).isEqualTo(DELAY)
@@ -822,50 +803,49 @@
@Test
fun set_haptic_on_errors() = runGenericTest {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"so sad",
messageAfterError = "",
authenticateAfterError = false,
hapticFeedback = true,
)
- val haptics by collectLastValue(viewModel.hapticsToPlay)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
fun plays_haptic_on_errors_unless_skipped() = runGenericTest {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"still sad",
messageAfterError = "",
authenticateAfterError = false,
hapticFeedback = false,
)
- val haptics by collectLastValue(viewModel.hapticsToPlay)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
}
@Test
fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"still sad",
messageAfterError = "",
authenticateAfterError = false,
hapticFeedback = true,
)
- val haptics by collectLastValue(viewModel.hapticsToPlay)
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
if (expectConfirmation) {
assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
} else {
- assertThat(haptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
}
}
@@ -875,15 +855,15 @@
block: suspend TestScope.() -> Unit = {},
) {
val errorMessage = "oh no!"
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val size by collectLastValue(viewModel.size)
- val canTryAgainNow by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgainNow by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
errorMessage,
authenticateAfterError = restart,
messageAfterError = helpAfterError,
@@ -913,13 +893,13 @@
@Test
fun no_errors_or_temporary_help_after_authenticated() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageIsShowing by collectLastValue(viewModel.isIndicatorMessageVisible)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageIsShowing by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
val verifyNoError = {
assertThat(authenticating).isFalse()
@@ -929,7 +909,7 @@
}
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
"error",
messageAfterError = "",
authenticateAfterError = false,
@@ -939,14 +919,14 @@
errorJob.join()
verifyNoError()
- val helpJob = launch { viewModel.showTemporaryHelp("hi") }
+ val helpJob = launch { kosmos.promptViewModel.showTemporaryHelp("hi") }
verifyNoError()
helpJob.join()
verifyNoError()
// persistent help is allowed
val stickyHelpMessage = "blah"
- viewModel.showHelp(stickyHelpMessage)
+ kosmos.promptViewModel.showHelp(stickyHelpMessage)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
assertThat(message).isEqualTo(PromptMessage.Help(stickyHelpMessage))
@@ -955,11 +935,11 @@
@Test
fun suppress_temporary_error() = runGenericTest {
- val messages by collectValues(viewModel.message)
+ val messages by collectValues(kosmos.promptViewModel.message)
for (error in listOf("never", "see", "me")) {
launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
error,
messageAfterError = "or me",
authenticateAfterError = false,
@@ -984,11 +964,11 @@
val errors = listOf("woot", "oh yeah", "nope")
val afterSuffix = "(after)"
val expectedErrorMessage = if (suppress) errors.first() else errors.last()
- val messages by collectValues(viewModel.message)
+ val messages by collectValues(kosmos.promptViewModel.message)
for (error in errors) {
launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
error,
messageAfterError = "$error $afterSuffix",
authenticateAfterError = false,
@@ -1017,15 +997,15 @@
@Test
fun authenticated_at_most_once_same_modality() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
@@ -1033,15 +1013,15 @@
@Test
fun authenticating_cannot_restart_after_authenticated() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
- viewModel.showAuthenticating("again!")
+ kosmos.promptViewModel.showAuthenticating("again!")
assertThat(authenticating).isFalse()
assertThat(authenticated?.isAuthenticated).isTrue()
@@ -1051,13 +1031,13 @@
fun confirm_authentication() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
@@ -1067,7 +1047,7 @@
confirm = true,
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1081,13 +1061,13 @@
fun second_authentication_acts_as_confirmation() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
@@ -1098,7 +1078,7 @@
)
if (testCase.modalities.hasSfps) {
- viewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
+ kosmos.promptViewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1114,15 +1094,15 @@
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
if (testCase.isCoex) {
- viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
+ kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
}
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticating).isFalse()
assertThat(canTryAgain).isFalse()
@@ -1136,7 +1116,7 @@
confirm = true,
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
} else if (testCase.isCoex) {
assertThat(authenticated?.isAuthenticatedAndConfirmed).isTrue()
}
@@ -1150,16 +1130,16 @@
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
if (testCase.isCoex) {
- viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
- viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP))
+ kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
+ kosmos.promptViewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP))
}
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val size by collectLastValue(viewModel.size)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
if (expectConfirmation) {
@@ -1169,7 +1149,7 @@
confirm = true,
)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(message).isEqualTo(PromptMessage.Empty)
assertButtonsVisible()
}
@@ -1181,18 +1161,18 @@
@Test
fun cannot_confirm_unless_authenticated() = runGenericTest {
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(authenticating).isTrue()
assertThat(authenticated?.isNotAuthenticated).isTrue()
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
// reconfirm should be a no-op
- viewModel.confirmAuthenticated()
- viewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
+ kosmos.promptViewModel.confirmAuthenticated()
assertThat(authenticating).isFalse()
assertThat(authenticated?.isNotAuthenticated).isFalse()
@@ -1201,36 +1181,36 @@
@Test
fun shows_help_before_authenticated() = runGenericTest {
val helpMessage = "please help yourself to some cookies"
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val size by collectLastValue(viewModel.size)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val size by collectLastValue(kosmos.promptViewModel.size)
- viewModel.showHelp(helpMessage)
+ kosmos.promptViewModel.showHelp(helpMessage)
assertThat(size).isEqualTo(PromptSize.MEDIUM)
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage))
assertThat(messageVisible).isTrue()
- assertThat(viewModel.isAuthenticating.first()).isFalse()
- assertThat(viewModel.isAuthenticated.first().isNotAuthenticated).isTrue()
+ assertThat(kosmos.promptViewModel.isAuthenticating.first()).isFalse()
+ assertThat(kosmos.promptViewModel.isAuthenticated.first().isNotAuthenticated).isTrue()
}
@Test
fun shows_help_after_authenticated() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
val helpMessage = "more cookies please"
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val size by collectLastValue(viewModel.size)
- val confirmationRequired by collectLastValue(viewModel.isConfirmationRequired)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val size by collectLastValue(kosmos.promptViewModel.size)
+ val confirmationRequired by collectLastValue(kosmos.promptViewModel.isConfirmationRequired)
if (testCase.isCoex && testCase.authenticatedByFingerprint) {
- viewModel.ensureFingerprintHasStarted(isDelayed = true)
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = true)
}
- viewModel.showAuthenticated(testCase.authenticatedModality, 0)
- viewModel.showHelp(helpMessage)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
+ kosmos.promptViewModel.showHelp(helpMessage)
assertThat(size).isEqualTo(PromptSize.MEDIUM)
@@ -1250,15 +1230,15 @@
val errorMessage = "bad"
val helpMessage = "again?"
val expectTryAgainButton = testCase.isFaceOnly
- val authenticating by collectLastValue(viewModel.isAuthenticating)
- val authenticated by collectLastValue(viewModel.isAuthenticated)
- val message by collectLastValue(viewModel.message)
- val messageVisible by collectLastValue(viewModel.isIndicatorMessageVisible)
- val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+ val authenticating by collectLastValue(kosmos.promptViewModel.isAuthenticating)
+ val authenticated by collectLastValue(kosmos.promptViewModel.isAuthenticated)
+ val message by collectLastValue(kosmos.promptViewModel.message)
+ val messageVisible by collectLastValue(kosmos.promptViewModel.isIndicatorMessageVisible)
+ val canTryAgain by collectLastValue(kosmos.promptViewModel.canTryAgainNow)
- viewModel.showAuthenticating("go")
+ kosmos.promptViewModel.showAuthenticating("go")
val errorJob = launch {
- viewModel.showTemporaryError(
+ kosmos.promptViewModel.showTemporaryError(
errorMessage,
messageAfterError = helpMessage,
authenticateAfterError = false,
@@ -1283,7 +1263,7 @@
assertButtonsVisible(negative = true, tryAgain = expectTryAgainButton)
val helpMessage2 = "foo"
- viewModel.showAuthenticating(helpMessage2, isRetry = true)
+ kosmos.promptViewModel.showAuthenticating(helpMessage2, isRetry = true)
assertThat(authenticating).isTrue()
assertThat(authenticated?.isAuthenticated).isFalse()
assertThat(message).isEqualTo(PromptMessage.Help(helpMessage2))
@@ -1293,10 +1273,10 @@
@Test
fun switch_to_credential_fallback() = runGenericTest {
- val size by collectLastValue(viewModel.size)
+ val size by collectLastValue(kosmos.promptViewModel.size)
// TODO(b/251476085): remove Spaghetti, migrate logic, and update this test
- viewModel.onSwitchToCredential()
+ kosmos.promptViewModel.onSwitchToCredential()
assertThat(size).isEqualTo(PromptSize.LARGE)
}
@@ -1304,15 +1284,15 @@
@Test
@EnableFlags(FLAG_BP_TALKBACK)
fun hint_for_talkback_guidance() = runGenericTest {
- val hint by collectLastValue(viewModel.accessibilityHint)
+ val hint by collectLastValue(kosmos.promptViewModel.accessibilityHint)
// Touches should fall outside of sensor area
- whenever(udfpsUtils.getTouchInNativeCoordinates(any(), any(), any()))
+ whenever(kosmos.udfpsUtils.getTouchInNativeCoordinates(any(), any(), any()))
.thenReturn(Point(0, 0))
- whenever(udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
+ whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
.thenReturn("Direction")
- viewModel.onAnnounceAccessibilityHint(
+ kosmos.promptViewModel.onAnnounceAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
true
)
@@ -1328,8 +1308,8 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionOverriddenByVerticalListContentView() =
runGenericTest(description = "test description", contentView = promptContentView) {
- val contentView by collectLastValue(viewModel.contentView)
- val description by collectLastValue(viewModel.description)
+ val contentView by collectLastValue(kosmos.promptViewModel.contentView)
+ val description by collectLastValue(kosmos.promptViewModel.description)
assertThat(description).isEqualTo("")
assertThat(contentView).isEqualTo(promptContentView)
@@ -1342,8 +1322,8 @@
description = "test description",
contentView = promptContentViewWithMoreOptionsButton
) {
- val contentView by collectLastValue(viewModel.contentView)
- val description by collectLastValue(viewModel.description)
+ val contentView by collectLastValue(kosmos.promptViewModel.contentView)
+ val description by collectLastValue(kosmos.promptViewModel.description)
assertThat(description).isEqualTo("")
assertThat(contentView).isEqualTo(promptContentViewWithMoreOptionsButton)
@@ -1353,8 +1333,8 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionWithoutContentView() =
runGenericTest(description = "test description") {
- val contentView by collectLastValue(viewModel.contentView)
- val description by collectLastValue(viewModel.description)
+ val contentView by collectLastValue(kosmos.promptViewModel.contentView)
+ val description by collectLastValue(kosmos.promptViewModel.description)
assertThat(description).isEqualTo("test description")
assertThat(contentView).isNull()
@@ -1364,7 +1344,7 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_nullIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat(logo).isNull()
}
@@ -1372,7 +1352,7 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_defaultWithOverrides() =
runGenericTest(packageName = packageNameForLogoWithOverrides) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
// 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
// applicationInfoWithIcon with defaultLogoIcon,
@@ -1385,14 +1365,14 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_defaultIsNull() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat(logo).isNull()
}
@Test
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_default() = runGenericTest {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat(logo).isEqualTo(defaultLogoIcon)
}
@@ -1401,7 +1381,7 @@
fun logo_resSetByApp() =
runGenericTest(logoRes = logoResFromApp) {
val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue()
}
@@ -1409,7 +1389,7 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_bitmapSetByApp() =
runGenericTest(logoBitmap = logoBitmapFromApp) {
- val logo by collectLastValue(viewModel.logo)
+ val logo by collectLastValue(kosmos.promptViewModel.logo)
assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
}
@@ -1417,7 +1397,7 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_emptyIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo("")
}
@@ -1425,14 +1405,14 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_defaultIsEmpty() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo("")
}
@Test
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_default() = runGenericTest {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo(defaultLogoDescription)
}
@@ -1440,57 +1420,57 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_setByApp() =
runGenericTest(logoDescription = logoDescriptionFromApp) {
- val logoDescription by collectLastValue(viewModel.logoDescription)
+ val logoDescription by collectLastValue(kosmos.promptViewModel.logoDescription)
assertThat(logoDescription).isEqualTo(logoDescriptionFromApp)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_rotation0() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Bottom)
} // TODO(b/335278136): Add test for no sensor landscape
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_forceLarge() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
- viewModel.onSwitchToCredential()
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.promptViewModel.onSwitchToCredential()
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Bottom)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_bottom_largeScreen() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
- displayStateRepository.setIsLargeScreen(true)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setIsLargeScreen(true)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Bottom)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_right_rotation90() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Right)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_left_rotation270() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ val position by collectLastValue(kosmos.promptViewModel.position)
assertThat(position).isEqualTo(PromptPosition.Left)
}
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun position_top_rotation180() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
- val position by collectLastValue(viewModel.position)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ val position by collectLastValue(kosmos.promptViewModel.position)
if (testCase.modalities.hasUdfps) {
assertThat(position).isEqualTo(PromptPosition.Top)
} else {
@@ -1501,18 +1481,18 @@
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_bottom() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0))
} // TODO(b/335278136): Add test for no sensor landscape
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_right() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (isSmall) {
assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0))
@@ -1527,10 +1507,10 @@
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_right_onlyShortTitle() =
runGenericTest(subtitle = "") {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (!isSmall && testCase.modalities.hasUdfps) {
assertThat(guidelineBounds)
@@ -1541,10 +1521,10 @@
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_left() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (isSmall) {
assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0))
@@ -1559,10 +1539,10 @@
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_left_onlyShortTitle() =
runGenericTest(subtitle = "") {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
val isSmall = testCase.shouldStartAsImplicitFlow
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (!isSmall && testCase.modalities.hasUdfps) {
assertThat(guidelineBounds)
@@ -1573,8 +1553,8 @@
@Test
@EnableFlags(FLAG_CONSTRAINT_BP)
fun guideline_top() = runGenericTest {
- displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
- val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
if (testCase.modalities.hasUdfps) {
assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0))
}
@@ -1582,11 +1562,11 @@
@Test
fun iconViewLoaded() = runGenericTest {
- val isIconViewLoaded by collectLastValue(viewModel.isIconViewLoaded)
+ val isIconViewLoaded by collectLastValue(kosmos.promptViewModel.isIconViewLoaded)
// TODO(b/328677869): Add test for noIcon logic.
assertThat(isIconViewLoaded).isFalse()
- viewModel.setIsIconViewLoaded(true)
+ kosmos.promptViewModel.setIsIconViewLoaded(true)
assertThat(isIconViewLoaded).isTrue()
}
@@ -1600,11 +1580,11 @@
credential: Boolean = false,
) {
runCurrent()
- assertThat(viewModel.isTryAgainButtonVisible.first()).isEqualTo(tryAgain)
- assertThat(viewModel.isConfirmButtonVisible.first()).isEqualTo(confirm)
- assertThat(viewModel.isCancelButtonVisible.first()).isEqualTo(cancel)
- assertThat(viewModel.isNegativeButtonVisible.first()).isEqualTo(negative)
- assertThat(viewModel.isCredentialButtonVisible.first()).isEqualTo(credential)
+ assertThat(kosmos.promptViewModel.isTryAgainButtonVisible.first()).isEqualTo(tryAgain)
+ assertThat(kosmos.promptViewModel.isConfirmButtonVisible.first()).isEqualTo(confirm)
+ assertThat(kosmos.promptViewModel.isCancelButtonVisible.first()).isEqualTo(cancel)
+ assertThat(kosmos.promptViewModel.isNegativeButtonVisible.first()).isEqualTo(negative)
+ assertThat(kosmos.promptViewModel.isCredentialButtonVisible.first()).isEqualTo(credential)
}
private fun runGenericTest(
@@ -1621,30 +1601,10 @@
) {
val topActivity = ComponentName(packageName, "test app")
runningTaskInfo.topActivity = topActivity
- whenever(activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
- selector =
- PromptSelectorInteractorImpl(
- fingerprintRepository,
- displayStateInteractor,
- promptRepository,
- lockPatternUtils
- )
- selector.resetPrompt(REQUEST_ID)
+ whenever(kosmos.activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
+ kosmos.promptSelectorInteractor.resetPrompt(REQUEST_ID)
- viewModel =
- PromptViewModel(
- displayStateInteractor,
- selector,
- mContext,
- udfpsOverlayInteractor,
- biometricStatusInteractor,
- udfpsUtils,
- iconProvider,
- activityTaskManager
- )
- iconViewModel = viewModel.iconViewModel
-
- selector.initializePrompt(
+ kosmos.promptSelectorInteractor.initializePrompt(
requireConfirmation = testCase.confirmationRequested,
allowCredentialFallback = allowCredentialFallback,
fingerprint = testCase.fingerprint,
@@ -1658,12 +1618,13 @@
packageName = packageName,
)
- biometricStatusRepository.setFingerprintAcquiredStatus(
+ kosmos.biometricStatusRepository.setFingerprintAcquiredStatus(
AcquiredFingerprintAuthenticationStatus(
AuthenticationReason.BiometricPromptAuthentication,
BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
)
)
+
// put the view model in the initial authenticating state, unless explicitly skipped
val startMode =
when {
@@ -1673,18 +1634,31 @@
}
when (startMode) {
FingerprintStartMode.Normal -> {
- viewModel.ensureFingerprintHasStarted(isDelayed = false)
- viewModel.showAuthenticating()
+ kosmos.promptViewModel.ensureFingerprintHasStarted(isDelayed = false)
+ kosmos.promptViewModel.showAuthenticating()
}
FingerprintStartMode.Delayed -> {
- viewModel.showAuthenticating()
+ kosmos.promptViewModel.showAuthenticating()
}
else -> {
/* skip */
}
}
- testScope.runTest { block() }
+ if (testCase.fingerprint?.isAnyUdfpsType == true) {
+ kosmos.testScope.collectLastValue(kosmos.udfpsOverlayInteractor.udfpsOverlayParams)
+ kosmos.testScope.runCurrent()
+ overrideUdfpsOverlayParams()
+ }
+
+ kosmos.testScope.runTest { block() }
+ }
+
+ private fun overrideUdfpsOverlayParams(isLandscape: Boolean = false) {
+ val authControllerCallback = authController.captureCallback()
+ authControllerCallback.onUdfpsLocationChanged(
+ mockUdfpsOverlayParams(isLandscape = isLandscape)
+ )
}
/** Obtain a MotionEvent with the specified MotionEvent action constant */
@@ -1705,11 +1679,43 @@
TestCase(
fingerprint =
fingerprintSensorPropertiesInternal(
+ sensorType = FingerprintSensorProperties.TYPE_REAR
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ ),
+ TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
strong = true,
sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
)
.first(),
authenticatedModality = BiometricModality.Fingerprint,
+ isInRearDisplayMode = false,
+ isDeviceFolded = false
+ ),
+ TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ isInRearDisplayMode = false,
+ isDeviceFolded = true
+ ),
+ TestCase(
+ fingerprint =
+ fingerprintSensorPropertiesInternal(
+ strong = true,
+ sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+ )
+ .first(),
+ authenticatedModality = BiometricModality.Fingerprint,
+ isInRearDisplayMode = true,
+ isDeviceFolded = false
),
TestCase(
fingerprint =
@@ -1783,6 +1789,8 @@
internal data class TestCase(
val fingerprint: FingerprintSensorPropertiesInternal? = null,
val face: FaceSensorPropertiesInternal? = null,
+ val isInRearDisplayMode: Boolean = false,
+ val isDeviceFolded: Boolean = false,
val authenticatedModality: BiometricModality,
val confirmationRequested: Boolean = false,
) {
@@ -1796,7 +1804,9 @@
face != null -> "face only"
else -> "?"
}
- return "[$modality, by: $authenticatedModality, confirm: $confirmationRequested]"
+ return "[$modality, isInRearDisplayMode: $isInRearDisplayMode, " +
+ "isDeviceFolded: $isDeviceFolded, by: $authenticatedModality, " +
+ "confirm: $confirmationRequested]"
}
fun expectConfirmation(atLeastOneFailure: Boolean): Boolean =
@@ -1872,4 +1882,25 @@
)
}
+private fun AuthController.captureCallback() =
+ withArgCaptor<AuthController.Callback> {
+ Mockito.verify(this@captureCallback).addCallback(capture())
+ }
+
+/** Get folded device configuration */
+fun getFoldedConfiguration(): Configuration {
+ val testConfig = Configuration()
+ val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
+ testConfig.smallestScreenWidthDp = folded
+ return testConfig
+}
+
+/** Get unfolded device configuration */
+fun getUnfoldedConfiguration(): Configuration {
+ val testConfig = Configuration()
+ val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
+ testConfig.smallestScreenWidthDp = unfolded
+ return testConfig
+}
+
internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
index d32d12c..a4936e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
@@ -37,7 +37,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.motion.createSysUiComposeMotionTestRule
-import com.android.systemui.scene.domain.interactor.sceneContainerStartable
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.testKosmos
import org.junit.Before
import org.junit.Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
index 21516d49..791f1f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
@@ -18,6 +18,7 @@
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.whenever
@@ -29,6 +30,7 @@
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
@@ -37,6 +39,7 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class ClipboardImageLoaderTest : SysuiTestCase() {
@Mock private lateinit var mockContext: Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
index ac1f90c..b3f4588 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
@@ -10,6 +10,7 @@
import android.service.controls.templates.ThumbnailTemplate
import android.view.LayoutInflater
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -20,10 +21,12 @@
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TemperatureControlBehaviorTest : SysuiTestCase() {
@Mock lateinit var controlsMetricsLogger: ControlsMetricsLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
index ef89752..82ad30e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.deviceentry.shared
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import junit.framework.Assert
import kotlin.reflect.full.declaredMembers
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class FaceAuthReasonTest : SysuiTestCase() {
@Test
fun testApiReasonToUiEvent_forAllReasons_isNotNull() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayMetricsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayMetricsRepositoryTest.kt
index dd741b4..d79db5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayMetricsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayMetricsRepositoryTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.util.DisplayMetrics
import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
@@ -33,9 +34,11 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class DisplayMetricsRepositoryTest : SysuiTestCase() {
private lateinit var underTest: DisplayMetricsRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 01868ae..1c327af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -126,6 +126,7 @@
// All subscribers are done, unregister should have been called.
verify(displayManager).unregisterDisplayListener(any())
}
+
@Test
fun onDisplayAdded_propagated() =
testScope.runTest {
@@ -457,6 +458,16 @@
assertThat(defaultDisplayOff).isFalse()
}
+ @Test
+ fun displayFlow_startsWithDefaultDisplayBeforeAnyEvent() =
+ testScope.runTest {
+ setDisplays(Display.DEFAULT_DISPLAY)
+
+ val value by latestDisplayFlowValue()
+
+ assertThat(value?.ids()).containsExactly(Display.DEFAULT_DISPLAY)
+ }
+
private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
// Wrapper to capture the displayListener.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
index a7d7b93..419f7ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
@@ -24,6 +24,7 @@
import android.view.Display;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,10 +34,12 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.concurrent.Executor;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class DozeScreenStatePreventingAdapterTest extends SysuiTestCase {
private Executor mExecutor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
index 240d2d7..5a89710 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
@@ -24,6 +24,7 @@
import android.view.Display;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,10 +34,12 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.concurrent.Executor;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class DozeSuspendScreenStatePreventingAdapterTest extends SysuiTestCase {
private Executor mExecutor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt
index 840eb46..5976292 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.dump
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
@@ -28,6 +29,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.never
@@ -39,6 +41,7 @@
import javax.inject.Provider
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DumpHandlerTest : SysuiTestCase() {
private lateinit var dumpHandler: DumpHandler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt
index 6d5226f..f331060 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.dump
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Dumpable
import com.android.systemui.SysuiTestCase
@@ -25,10 +26,12 @@
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DumpManagerTest : SysuiTestCase() {
@Mock private lateinit var dumpable1: Dumpable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpsysTableLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpsysTableLoggerTest.kt
index 1d2afe4..d09928b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpsysTableLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpsysTableLoggerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.dump
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -24,11 +25,13 @@
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import java.io.PrintWriter
import java.io.StringWriter
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DumpsysTableLoggerTest : SysuiTestCase() {
private val logger = DumpsysTableLogger(
TEST_SECTION_NAME,
@@ -143,4 +146,4 @@
List(TEST_COLUMNS.size) { col ->
"data$col$row"
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferFreezerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferFreezerTest.kt
index 3b4888f..0cd2b9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferFreezerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferFreezerTest.kt
@@ -19,6 +19,7 @@
import android.content.BroadcastReceiver
import android.content.IntentFilter
import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -30,6 +31,7 @@
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
@@ -40,6 +42,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class LogBufferFreezerTest : SysuiTestCase() {
lateinit var freezer: LogBufferFreezer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt
index 3ff7202..ae6b337 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.dump
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpHandler.Companion.dump
@@ -40,6 +41,7 @@
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito
@@ -48,6 +50,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class LogEulogizerTest : SysuiTestCase() {
lateinit var eulogizer: LogBufferEulogizer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ConditionalRestarterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ConditionalRestarterTest.kt
index 52c6e22..9f238e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/ConditionalRestarterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ConditionalRestarterTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.flags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -26,6 +27,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -35,6 +37,7 @@
* Be careful with the {FeatureFlagsReleaseRestarter} in this test. It has a call to System.exit()!
*/
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ConditionalRestarterTest : SysuiTestCase() {
private lateinit var restarter: ConditionalRestarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index dbe59e6..a1fe0f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -23,6 +23,7 @@
import android.content.res.Resources.NotFoundException
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD
import com.android.systemui.SysuiTestCase
@@ -39,6 +40,7 @@
import org.junit.Assert
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
@@ -55,6 +57,7 @@
* the default.
*/
@SmallTest
+@RunWith(AndroidJUnit4::class)
class FeatureFlagsClassicDebugTest : SysuiTestCase() {
private lateinit var mFeatureFlagsClassicDebug: FeatureFlagsClassicDebug
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicReleaseTest.kt
index 943e212..ad8bedb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicReleaseTest.kt
@@ -17,12 +17,14 @@
import android.content.pm.PackageManager.NameNotFoundException
import android.content.res.Resources
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.never
@@ -34,6 +36,7 @@
* overriding, and should never return any value other than the one provided as the default.
*/
@SmallTest
+@RunWith(AndroidJUnit4::class)
class FeatureFlagsClassicReleaseTest : SysuiTestCase() {
private lateinit var mFeatureFlagsClassicRelease: FeatureFlagsClassicRelease
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
index d500dd2..dd56fa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
@@ -16,18 +16,21 @@
package com.android.systemui.flags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import java.io.PrintWriter
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class FlagCommandTest : SysuiTestCase() {
@Mock private lateinit var featureFlags: FeatureFlagsClassicDebug
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
index 5e87a6f..593de37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
@@ -19,6 +19,7 @@
import android.database.ContentObserver
import android.net.Uri
import android.os.Handler
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -30,6 +31,7 @@
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -42,6 +44,7 @@
* overriding, and should never return any value other than the one provided as the default.
*/
@SmallTest
+@RunWith(AndroidJUnit4::class)
class FlagManagerTest : SysuiTestCase() {
private lateinit var mFlagManager: FlagManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/NotOccludedConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/NotOccludedConditionTest.kt
index 755cc46..46b4c4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/NotOccludedConditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/NotOccludedConditionTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.flags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -30,6 +31,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -39,6 +41,7 @@
*/
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class NotOccludedConditionTest : SysuiTestCase() {
private lateinit var condition: NotOccludedCondition
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/PluggedInConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/PluggedInConditionTest.kt
index 0fdda08..0f727cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/PluggedInConditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/PluggedInConditionTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.flags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -26,6 +27,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -36,6 +38,7 @@
* Be careful with the {FeatureFlagsReleaseRestarter} in this test. It has a call to System.exit()!
*/
@SmallTest
+@RunWith(AndroidJUnit4::class)
class PluggedInConditionTest : SysuiTestCase() {
private lateinit var condition: PluggedInCondition
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
index 3c965ce..1ab945a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/RestartDozeListenerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.flags
import android.os.PowerManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -27,6 +28,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
@@ -37,6 +39,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class RestartDozeListenerTest : SysuiTestCase() {
lateinit var restartDozeListener: RestartDozeListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ScreenIdleConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ScreenIdleConditionTest.kt
index 0116e53..df03882 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/ScreenIdleConditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ScreenIdleConditionTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.flags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -27,6 +28,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -35,6 +37,7 @@
* Be careful with the {FeatureFlagsReleaseRestarter} in this test. It has a call to System.exit()!
*/
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ScreenIdleConditionTest : SysuiTestCase() {
private lateinit var condition: ScreenIdleCondition
diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
index 8a29217..008c68d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
@@ -2,6 +2,7 @@
import android.app.Fragment
import android.os.Looper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -9,8 +10,10 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class FragmentServiceTest : SysuiTestCase() {
private val fragmentHostManagerFactory: FragmentHostManager.Factory = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
index 7f55d38..dc019b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java
@@ -37,6 +37,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -48,6 +49,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@@ -55,6 +57,7 @@
@LargeTest
@FlakyTest(bugId = 176891566)
+@RunWith(AndroidJUnit4.class)
public class GlobalActionsImeTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
index 3a86868..2735d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
@@ -20,6 +20,7 @@
import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
@@ -33,14 +34,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class KeyboardDockingIndicationViewModelTest : SysuiTestCase() {
private val testScope = TestScope(StandardTestDispatcher())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt
new file mode 100644
index 0000000..3caa8f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.keyboard.shortcut.data.repository
+
+import android.hardware.input.fakeInputManager
+import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
+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.keyboard.shortcut.shared.model.ShortcutHelperState
+import com.android.systemui.keyboard.shortcut.shortcutHelperStateRepository
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShortcutHelperStateRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private val repo = kosmos.shortcutHelperStateRepository
+ private val helper = kosmos.shortcutHelperTestHelper
+ private val testScope = kosmos.testScope
+ private val fakeInputManager = kosmos.fakeInputManager
+
+ @Test
+ fun state_activeThroughToggle_emitsActiveWithDeviceIdFromEvent() =
+ testScope.runTest {
+ val deviceId = 123
+ val state by collectLastValue(repo.state)
+
+ helper.toggle(deviceId)
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(deviceId))
+ }
+
+ @Test
+ fun state_activeThroughActivity_noKeyboardActive_emitsActiveWithVirtualDeviceId() =
+ testScope.runTest {
+ val state by collectLastValue(repo.state)
+
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD))
+ }
+
+ @Test
+ fun state_activeThroughActivity_virtualKeyboardActive_emitsActiveWithVirtualDeviceId() =
+ testScope.runTest {
+ val state by collectLastValue(repo.state)
+
+ fakeInputManager.addVirtualKeyboard()
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD))
+ }
+
+ @Test
+ fun state_activeThroughActivity_physicalKeyboardActive_emitsActiveWithDeviceId() =
+ testScope.runTest {
+ val deviceId = 456
+ val state by collectLastValue(repo.state)
+
+ fakeInputManager.addPhysicalKeyboard(deviceId)
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(deviceId))
+ }
+
+ @Test
+ fun state_activeThroughActivity_physicalKeyboardDisabled_emitsActiveWithVirtualDeviceId() =
+ testScope.runTest {
+ val deviceId = 456
+ val state by collectLastValue(repo.state)
+
+ fakeInputManager.addPhysicalKeyboard(deviceId)
+ fakeInputManager.inputManager.disableInputDevice(deviceId)
+ helper.showFromActivity()
+
+ assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
new file mode 100644
index 0000000..9c9e48e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.keyboard.shortcut.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesInteractor
+import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcutsSource
+import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() {
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private val kosmos = testKosmos().also { it.testDispatcher = UnconfinedTestDispatcher() }
+ private val testScope = kosmos.testScope
+ private val interactor = kosmos.shortcutHelperCategoriesInteractor
+ private val helper = kosmos.shortcutHelperTestHelper
+ private val systemShortcutsSource = kosmos.shortcutHelperSystemShortcutsSource
+ private val multitaskingShortcutsSource = kosmos.shortcutHelperMultiTaskingShortcutsSource
+
+ @Test
+ fun categories_emptyByDefault() =
+ testScope.runTest {
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ assertThat(categories).isEmpty()
+ }
+
+ @Test
+ fun categories_stateActive_emitsAllCategoriesInOrder() =
+ testScope.runTest {
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ systemShortcutsSource.systemShortcutsCategory(),
+ multitaskingShortcutsSource.multitaskingShortcutCategory()
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateInactiveAfterActive_emitsEmpty() =
+ testScope.runTest {
+ val categories by collectLastValue(interactor.shortcutCategories)
+ helper.showFromActivity()
+ helper.hideFromActivity()
+
+ assertThat(categories).isEmpty()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
new file mode 100644
index 0000000..c4eabd8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.keyguard.ui.binder
+
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper
+import android.view.View
+import android.view.layoutInflater
+import android.view.mockedLayoutInflater
+import android.view.windowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.isNull
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AlternateBouncerViewBinderTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ private val mockedAltBouncerView =
+ spy(kosmos.layoutInflater.inflate(R.layout.alternate_bouncer, null, false))
+
+ @Before
+ fun setup() {
+ whenever(
+ kosmos.mockedLayoutInflater.inflate(
+ eq(R.layout.alternate_bouncer),
+ isNull(),
+ anyBoolean()
+ )
+ )
+ .thenReturn(mockedAltBouncerView)
+ kosmos.alternateBouncerViewBinder.start()
+ }
+
+ @Test
+ @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ fun addViewToWindowManager() {
+ testScope.runTest {
+ kosmos.givenCanShowAlternateBouncer()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ testScope,
+ )
+ verify(kosmos.windowManager).addView(any(), any())
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ fun viewRemovedImmediatelyIfAlreadyAttachedToWindow() {
+ testScope.runTest {
+ kosmos.givenCanShowAlternateBouncer()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ testScope,
+ )
+ verify(kosmos.windowManager).addView(any(), any())
+ whenever(mockedAltBouncerView.isAttachedToWindow).thenReturn(true)
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+ verify(kosmos.windowManager).removeView(any())
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ fun viewNotRemovedUntilAttachedToWindow() {
+ testScope.runTest {
+ kosmos.givenCanShowAlternateBouncer()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ testScope,
+ )
+ verify(kosmos.windowManager).addView(any(), any())
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ verify(kosmos.windowManager, never()).removeView(any())
+ givenAltBouncerViewAttachedToWindow()
+ verify(kosmos.windowManager).removeView(any())
+ }
+ }
+
+ private fun givenAltBouncerViewAttachedToWindow() {
+ val attachStateChangeListenerCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+ verify(mockedAltBouncerView, atLeastOnce())
+ .addOnAttachStateChangeListener(attachStateChangeListenerCaptor.capture())
+ attachStateChangeListenerCaptor.allValues.onEach {
+ it.onViewAttachedToWindow(mockedAltBouncerView)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
index 7787a7f..9055495 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
@@ -18,6 +18,7 @@
import android.testing.TestableLooper.RunWithLooper
import android.view.RemoteAnimationTarget
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
import com.android.systemui.SysuiTestCase
@@ -34,6 +35,7 @@
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.doAnswer
@@ -42,6 +44,7 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@kotlinx.coroutines.ExperimentalCoroutinesApi
+@RunWith(AndroidJUnit4::class)
class KeyguardSurfaceBehindParamsApplierTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule(this)
@@ -155,4 +158,4 @@
// Releasing the surface should immediately cancel animators.
assertFalse(checkNotNull(isAnimatingSurface))
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index bdc5fc3..49a72e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -46,8 +46,8 @@
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTouchHandlingInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
@@ -58,6 +58,7 @@
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.pulsingGestureListener
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -211,8 +212,8 @@
dumpManager = mock(),
userHandle = UserHandle.SYSTEM,
)
- val keyguardLongPressInteractor =
- KeyguardLongPressInteractor(
+ val keyguardTouchHandlingInteractor =
+ KeyguardTouchHandlingInteractor(
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor = kosmos.keyguardTransitionInteractor,
@@ -221,6 +222,7 @@
featureFlags = featureFlags,
broadcastDispatcher = broadcastDispatcher,
accessibilityManager = accessibilityManager,
+ pulsingGestureListener = kosmos.pulsingGestureListener,
)
underTest =
KeyguardBottomAreaViewModel(
@@ -246,13 +248,13 @@
),
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
burnInHelperWrapper = burnInHelperWrapper,
- longPressViewModel =
- KeyguardLongPressViewModel(
- interactor = keyguardLongPressInteractor,
+ keyguardTouchHandlingViewModel =
+ KeyguardTouchHandlingViewModel(
+ interactor = keyguardTouchHandlingInteractor,
),
settingsMenuViewModel =
KeyguardSettingsMenuViewModel(
- interactor = keyguardLongPressInteractor,
+ interactor = keyguardTouchHandlingInteractor,
),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoSettingsFormatTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoSettingsFormatTest.kt
index 02c9deb..aa4a6d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoSettingsFormatTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoSettingsFormatTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log.echo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.core.LogLevel
@@ -24,8 +25,10 @@
import com.android.systemui.log.echo.EchoOverrideType.TAG
import kotlin.test.assertEquals
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class LogcatEchoSettingsFormatTest : SysuiTestCase() {
private val format = LogcatEchoSettingFormat()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoTrackerDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoTrackerDebugTest.kt
index 7967134..a5f50af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoTrackerDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/echo/LogcatEchoTrackerDebugTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log.echo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.core.LogLevel.DEBUG
@@ -39,11 +40,13 @@
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class LogcatEchoTrackerDebugTest : SysuiTestCase() {
private val dispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
index 1d182cf..e55cb12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log.table
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
@@ -35,9 +36,11 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class LogDiffsForTableTest : SysuiTestCase() {
private val testDispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
index 43e430f..8d60811 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log.table
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
@@ -23,8 +24,10 @@
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TableChangeTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
index 8ba7643..8c62bc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log.table
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -26,9 +27,11 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Test
+import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TableLogBufferFactoryTest : SysuiTestCase() {
private val dumpManager: DumpManager = mock()
private val systemClock = FakeSystemClock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 5c9a003..ace562b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log.table
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogcatEchoTracker
@@ -35,9 +36,11 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TableLogBufferTest : SysuiTestCase() {
private lateinit var underTest: TableLogBuffer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index 544350c..1d4b090 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -79,7 +79,7 @@
USER_ID, true, APP, null, ARTIST, TITLE, null,
new ArrayList<>(), new ArrayList<>(), null, PACKAGE, null, null, null, true, null,
MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L, 0L,
- InstanceId.fakeInstanceId(-1), -1, false, null);
+ InstanceId.fakeInstanceId(-1), -1, false, null, -1, false);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME, null, false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 4da56b5..850e2e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -16,8 +16,11 @@
package com.android.systemui.media.controls.domain.pipeline
+import android.R
import android.app.smartspace.SmartspaceAction
+import android.graphics.drawable.Icon
import android.os.Bundle
+import android.os.Process
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -36,7 +39,11 @@
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.controls.util.MediaUiEventLogger
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.testKosmos
@@ -101,7 +108,12 @@
private lateinit var dataGuest: MediaData
private lateinit var dataPrivateProfile: MediaData
private val clock = FakeSystemClock()
- private val repository: MediaFilterRepository = kosmos.mediaFilterRepository
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
+ private val repository: MediaFilterRepository =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaFilterRepository
+ }
private val mediaLoadingLogger = kosmos.mockMediaLoadingLogger
@Before
@@ -146,6 +158,8 @@
whenever(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
whenever(smartspaceData.recommendations)
.thenReturn(listOf(smartspaceMediaRecommendationItem))
+ whenever(smartspaceMediaRecommendationItem.icon)
+ .thenReturn(Icon.createWithResource(context, R.drawable.ic_media_play))
whenever(smartspaceData.headphoneConnectionTimeMillis)
.thenReturn(clock.currentTimeMillis() - 100)
whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
@@ -533,8 +547,22 @@
@Test
fun onSwipeToDismiss_setsTimedOut() {
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- mediaDataFilter.onSwipeToDismiss()
+ mediaDataFilter.onSwipeToDismiss(1)
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ eq(MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ eq(true)
+ )
verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true))
}
@@ -740,11 +768,8 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
- val controlCommonModel =
- MediaCommonModel.MediaControl(
- MediaDataLoadingModel.Loaded(dataMain.instanceId),
- true
- )
+ val mediaLoadingModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
+ var controlCommonModel = MediaCommonModel.MediaControl(mediaLoadingModel, true)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
@@ -758,7 +783,15 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should treat the media as active instead
- val dataCurrentAndActive = dataCurrent.copy(active = true)
+ val dataCurrentAndActive =
+ dataMain.copy(active = true, lastActive = clock.elapsedRealtime())
+ controlCommonModel =
+ controlCommonModel.copy(
+ mediaLoadingModel.copy(
+ receivedSmartspaceCardLatency = 100,
+ isSsReactivated = true
+ )
+ )
assertThat(currentMedia).containsExactly(controlCommonModel)
assertThat(
hasActiveMediaOrRecommendation(
@@ -800,11 +833,8 @@
val currentMedia by collectLastValue(repository.currentMedia)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
- val controlCommonModel =
- MediaCommonModel.MediaControl(
- MediaDataLoadingModel.Loaded(dataMain.instanceId),
- true
- )
+ val mediaLoadingModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
+ var controlCommonModel = MediaCommonModel.MediaControl(mediaLoadingModel, true)
val recsCommonModel =
MediaCommonModel.MediaRecommendations(
SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
@@ -824,7 +854,8 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should treat the media as active instead
- val dataCurrentAndActive = dataCurrent.copy(active = true)
+ val dataCurrentAndActive =
+ dataMain.copy(active = true, lastActive = clock.elapsedRealtime())
verify(listener)
.onMediaDataLoaded(
eq(KEY),
@@ -849,6 +880,13 @@
)
.isTrue()
// Smartspace update should also be propagated but not prioritized.
+ controlCommonModel =
+ controlCommonModel.copy(
+ mediaLoadingModel.copy(
+ receivedSmartspaceCardLatency = 100,
+ isSsReactivated = true
+ )
+ )
assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
verify(listener)
.onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
@@ -909,7 +947,8 @@
runCurrent()
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- val dataCurrentAndActive = dataCurrent.copy(active = true)
+ val dataCurrentAndActive =
+ dataMain.copy(active = true, lastActive = clock.elapsedRealtime())
verify(listener)
.onMediaDataLoaded(
eq(KEY),
@@ -1042,11 +1081,27 @@
targetId = SMARTSPACE_KEY,
isActive = true,
packageName = SMARTSPACE_PACKAGE,
- recommendations = listOf(smartspaceMediaRecommendationItem),
+ recommendations =
+ listOf(
+ smartspaceMediaRecommendationItem,
+ smartspaceMediaRecommendationItem,
+ smartspaceMediaRecommendationItem
+ ),
)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
- mediaDataFilter.onSwipeToDismiss()
+ mediaDataFilter.onSwipeToDismiss(1)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT,
+ SmallHash.hash(data.targetId),
+ Process.INVALID_UID,
+ surface = 1,
+ cardinality = 1,
+ isRecommendationCard = true,
+ isSwipeToDismiss = true
+ )
verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY))
verify(mediaDataProcessor, never())
.dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
@@ -1063,11 +1118,8 @@
MediaCommonModel.MediaRecommendations(
SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY)
)
- val controlCommonModel =
- MediaCommonModel.MediaControl(
- MediaDataLoadingModel.Loaded(dataMain.instanceId),
- true
- )
+ val mediaLoadingModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
+ var controlCommonModel = MediaCommonModel.MediaControl(mediaLoadingModel, true)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
@@ -1086,7 +1138,15 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should treat the media as active instead
- val dataCurrentAndActive = dataCurrent.copy(active = true)
+ val dataCurrentAndActive =
+ dataMain.copy(active = true, lastActive = clock.elapsedRealtime())
+ controlCommonModel =
+ controlCommonModel.copy(
+ mediaLoadingModel.copy(
+ receivedSmartspaceCardLatency = 100,
+ isSsReactivated = true
+ )
+ )
verify(listener)
.onMediaDataLoaded(
eq(KEY),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
index 473dc47..868145d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
@@ -18,6 +18,7 @@
import android.app.smartspace.SmartspaceAction
import android.graphics.drawable.Icon
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
@@ -26,8 +27,10 @@
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class SmartspaceMediaDataTest : SysuiTestCase() {
private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index ccf926a..f8358c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.MediaTestUtils
@@ -158,6 +159,7 @@
testDispatcher = UnconfinedTestDispatcher()
mediaCarouselController =
MediaCarouselController(
+ applicationScope = kosmos.applicationCoroutineScope,
context = context,
mediaControlPanelFactory = mediaControlPanelFactory,
visualStabilityProvider = visualStabilityProvider,
@@ -195,10 +197,10 @@
MediaPlayerData.clear()
FakeExecutor.exhaustExecutors(bgExecutor)
verify(globalSettings)
- .registerContentObserverSync(
- eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
- capture(settingsObserverCaptor)
- )
+ .registerContentObserverSync(
+ eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
+ capture(settingsObserverCaptor)
+ )
}
@After
@@ -894,7 +896,10 @@
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
- val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+ val settingsJob =
+ mediaCarouselController.listenForLockscreenSettingChanges(
+ kosmos.applicationCoroutineScope
+ )
secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
@@ -921,7 +926,10 @@
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
- val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+ val settingsJob =
+ mediaCarouselController.listenForLockscreenSettingChanges(
+ kosmos.applicationCoroutineScope
+ )
secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index ecc456c..a770722 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -40,6 +40,7 @@
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
@@ -1771,8 +1772,40 @@
verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId))
}
+ @EnableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
@Test
- fun tapContentView_showOverLockscreen_openActivity() {
+ fun tapContentView_showOverLockscreen_openActivity_withOriginAnimation() {
+ // WHEN we are on lockscreen and this activity can show over lockscreen
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true)
+
+ val clickIntent = mock(Intent::class.java)
+ val pendingIntent = mock(PendingIntent::class.java)
+ whenever(pendingIntent.intent).thenReturn(clickIntent)
+ val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java)
+ val data = mediaData.copy(clickIntent = pendingIntent)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(data, KEY)
+ verify(viewHolder.player).setOnClickListener(captor.capture())
+
+ // THEN it sends the PendingIntent without dismissing keyguard first,
+ // and does not use the Intent directly (see b/271845008)
+ captor.value.onClick(viewHolder.player)
+ verify(activityStarter)
+ .startPendingIntentMaybeDismissingKeyguard(
+ eq(pendingIntent),
+ eq(true),
+ eq(null),
+ any(),
+ eq(null),
+ eq(null),
+ )
+ verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any())
+ }
+
+ @DisableFlags(Flags.FLAG_MEDIA_LOCKSCREEN_LAUNCH_ANIMATION)
+ @Test
+ fun tapContentView_showOverLockscreen_openActivity_withoutOriginAnimation() {
// WHEN we are on lockscreen and this activity can show over lockscreen
whenever(keyguardStateController.isShowing).thenReturn(true)
whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
index 2a8967e..64acc59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
@@ -24,6 +24,7 @@
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.media.DeviceIconUtil
import com.android.settingslib.media.LocalMediaManager
@@ -35,6 +36,7 @@
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
@@ -45,6 +47,7 @@
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
private lateinit var muteAwaitConnectionManager: MediaMuteAwaitConnectionManager
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
index d9453d6..b9aa029 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
@@ -2,6 +2,7 @@
import android.media.INearbyMediaDevicesProvider
import android.media.INearbyMediaDevicesUpdateCallback
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import android.media.NearbyDevice
@@ -10,6 +11,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.anyInt
@@ -20,6 +22,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class NearbyMediaDevicesManagerTest : SysuiTestCase() {
private lateinit var manager: NearbyMediaDevicesManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index d828193..77807bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -19,6 +19,7 @@
import android.app.StatusBarManager
import android.content.Context
import android.media.MediaRoute2Info
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
@@ -35,6 +36,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -43,6 +45,7 @@
import java.util.concurrent.Executor
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaTttCommandLineHelperTest : SysuiTestCase() {
private val inlineExecutor = Executor { command -> command.run() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
index b322bb7..ab5c893 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtilsTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media.taptotransfer.common
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -27,9 +28,11 @@
import java.io.StringWriter
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaTttLoggerUtilsTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
index 5c6d913..02123ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -19,6 +19,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -30,12 +31,14 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaTttUtilsTest : SysuiTestCase() {
private lateinit var appIconFromPackageName: Drawable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
index 64f3fd3..cee71b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverLoggerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media.taptotransfer.receiver
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -27,9 +28,11 @@
import java.io.StringWriter
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaTttReceiverLoggerTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt
index f557713..352443a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttReceiverUiEventLoggerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media.taptotransfer.receiver
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
@@ -7,8 +8,10 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaTttReceiverUiEventLoggerTest : SysuiTestCase() {
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var logger: MediaTttReceiverUiEventLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
index ee3704c..af8b3ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLoggerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media.taptotransfer.sender
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
@@ -28,9 +29,11 @@
import java.io.StringWriter
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaTttSenderLoggerTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
index bf26a2f..30b578a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media.taptotransfer.sender
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
@@ -7,8 +8,10 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaTttSenderUiEventLoggerTest : SysuiTestCase() {
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var logger: MediaTttSenderUiEventLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
index f4c5ccf..b337ccf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
@@ -22,6 +22,7 @@
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX
import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN
@@ -36,6 +37,7 @@
import java.util.Optional
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.any
@@ -43,6 +45,7 @@
import org.mockito.Mockito.verify
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MediaProjectionRecentsViewControllerTest : SysuiTestCase() {
@get:Rule val expect: Expect = Expect.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index 9630ee3..55e52b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -27,6 +27,7 @@
import android.view.WindowMetrics
import androidx.core.view.WindowInsetsCompat.Type
import androidx.lifecycle.LifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
@@ -38,8 +39,10 @@
import kotlin.math.min
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TaskPreviewSizeProviderTest : SysuiTestCase() {
private val lifecycleOwner = mock<LifecycleOwner>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt
index fe18454..cc722aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt
@@ -5,14 +5,17 @@
import android.view.WindowManager
import android.view.WindowMetrics
import androidx.core.view.WindowInsetsCompat
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class WindowMetricsProviderImplTest : SysuiTestCase() {
private val windowManager = mock<WindowManager>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 2ff660f..bfbb7ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -36,6 +36,7 @@
import android.content.ComponentName;
import android.content.res.Configuration;
+import android.os.Handler;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -65,6 +66,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -120,6 +123,10 @@
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
@Mock
NotificationShadeWindowController mNotificationShadeWindowController;
+ @Mock
+ Handler mBgHandler;
+
+ @Captor ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
ConfigurationController mConfigurationController = new FakeConfigurationController();
private AccessibilityManager.AccessibilityServicesStateChangeListener
@@ -149,7 +156,7 @@
() -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
mDisplayTracker, mNotificationShadeWindowController, mConfigurationController,
- mDumpManager, mCommandQueue, mSynchronousExecutor);
+ mDumpManager, mCommandQueue, mSynchronousExecutor, mBgHandler);
}
@Test
@@ -203,8 +210,10 @@
.updateAccessibilityServicesState();
verify(mNavbarTaskbarStateUpdater, times(1))
.updateAssistantAvailable(anyBoolean(), anyBoolean());
+ verify(mBgHandler).post(mRunnableArgumentCaptor.capture());
+ mRunnableArgumentCaptor.getValue().run();
verify(mNavbarTaskbarStateUpdater, times(1))
- .updateRotationWatcherState(anyInt());
+ .updateRotationWatcherState(anyInt(), anyBoolean());
verify(mNavbarTaskbarStateUpdater, times(1))
.updateWallpaperVisibility(anyBoolean(), anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index d5361ac..df8eafe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -21,6 +21,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
+import static com.android.wm.shell.Flags.enableTaskbarOnPhones;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -142,10 +143,11 @@
@Test
public void testCreateNavigationBarsIncludeDefaultTrue() {
- assumeFalse(enableTaskbarNavbarUnification());
+ assumeFalse(enableTaskbarNavbarUnification() && enableTaskbarOnPhones());
// Large screens may be using taskbar and the logic is different
mNavigationBarController.mIsLargeScreen = false;
+ mNavigationBarController.mIsPhone = true;
doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any());
mNavigationBarController.createNavigationBars(true, null);
@@ -291,6 +293,17 @@
@Test
public void testShouldRenderTaskbar_taskbarNotRenderedOnPhone() {
+ assumeFalse(enableTaskbarOnPhones());
+
+ mNavigationBarController.mIsLargeScreen = false;
+ mNavigationBarController.mIsPhone = true;
+ assertFalse(mNavigationBarController.supportsTaskbar());
+ }
+
+ @Test
+ public void testShouldRenderTaskbar_taskbarRenderedOnPhone() {
+ assumeTrue(enableTaskbarNavbarUnification() && enableTaskbarOnPhones());
+
mNavigationBarController.mIsLargeScreen = false;
mNavigationBarController.mIsPhone = true;
assertFalse(mNavigationBarController.supportsTaskbar());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 2b60f65..982a269 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -179,6 +179,9 @@
private SysUiState mMockSysUiState;
@Mock
private Handler mHandler;
+
+ @Mock
+ private Handler mBgHandler;
@Mock
private UserTracker mUserTracker;
@Mock
@@ -277,7 +280,8 @@
mEdgeBackGestureHandlerFactory, mock(IWindowManager.class),
mock(UserTracker.class), mock(DisplayTracker.class),
mNotificationShadeWindowController, mock(ConfigurationController.class),
- mock(DumpManager.class), mock(CommandQueue.class), mSynchronousExecutor));
+ mock(DumpManager.class), mock(CommandQueue.class), mSynchronousExecutor,
+ mBgHandler));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index fbfd35f..c7a92d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -36,7 +36,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index bba275e..eae6cdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -1,11 +1,13 @@
package com.android.systemui.navigationbar
import android.app.ActivityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.model.SysUiState
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.shared.system.TaskStackChangeListeners
@@ -19,6 +21,7 @@
import java.util.Optional
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito.any
@@ -30,6 +33,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TaskbarDelegateTest : SysuiTestCase() {
val DISPLAY_ID = 0;
val MODE_GESTURE = 0;
@@ -67,6 +71,8 @@
lateinit var mCurrentSysUiState: NavBarHelper.CurrentSysuiState
@Mock
lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
+ lateinit var mStatusBarStateController: StatusBarStateController
@Before
fun setup() {
@@ -77,7 +83,7 @@
`when`(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState)
mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance()
mTaskbarDelegate = TaskbarDelegate(context, mLightBarControllerFactory,
- mStatusBarKeyguardViewManager)
+ mStatusBarKeyguardViewManager, mStatusBarStateController)
mTaskbarDelegate.setDependencies(mCommandQueue, mOverviewProxyService, mNavBarHelper,
mNavigationModeController, mSysUiState, mDumpManager, mAutoHideController,
mLightBarController, mOptionalPip, mBackAnimation, mTaskStackChangeListeners)
@@ -108,4 +114,4 @@
ArgumentMatchers.eq(true)
)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
index 9e5d3bd..cc3e27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -26,9 +27,11 @@
import java.io.PrintWriter
import java.io.StringWriter
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@SmallTest
+@RunWith(AndroidJUnit4::class)
class QSDisableFlagsLoggerTest : SysuiTestCase() {
private val buffer =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
index 1c86638..03483c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
@@ -20,6 +20,8 @@
import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -279,4 +281,23 @@
verify(mTileLifecycle, never()).onStopListening();
verify(mTileLifecycle, never()).executeSetBindService(false);
}
+
+ @Test
+ public void testNoExtraPendingBindIfAlreadyBound() {
+ mTileServiceManager.startLifecycleManagerAndAddTile();
+
+ // As part of adding the tile, it will be bound and it will send a start successful to
+ // TileServices. startSuccessful will clear pending bind
+ mTileServiceManager.clearPendingBind();
+
+ // Assume we are still bound
+ when(mTileLifecycle.isBound()).thenReturn(true);
+
+ // And we want to bind again
+ mTileServiceManager.setBindAllowed(true);
+ mTileServiceManager.setBindRequested(true);
+
+ // Then the tile doesn't have pending bind
+ assertThat(mTileServiceManager.hasPendingBind()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 50cf5cc5..9f12b18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -39,7 +39,6 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
@@ -54,14 +53,11 @@
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository;
-import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import kotlinx.coroutines.test.TestScope;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -105,12 +101,10 @@
@Mock
private QsEventLogger mUiEventLogger;
- private WifiInteractor mWifiInteractor;
private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
private final FakeConnectivityRepository mConnectivityRepository =
new FakeConnectivityRepository();
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
- private final TestScope mTestScope = TestScopeProvider.getTestScope();
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -172,8 +166,7 @@
@Test
public void testStateActive_wifiEnabledAndCasting() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -232,8 +225,7 @@
@Test
public void stateActive_wifiConnectedAndCasting_newPipeline() {
createAndStartTileNewImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -248,8 +240,7 @@
@Test
public void stateActive_ethernetConnectedAndCasting_newPipeline() {
createAndStartTileNewImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -286,8 +277,7 @@
@Test
public void testStateActive_hotspotEnabledAndConnectedAndCasting() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -309,9 +299,13 @@
@Test
public void testHandleClick_castDevicePresent() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaRouter.RouteInfo.class);
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(MediaRouter.RouteInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -327,9 +321,13 @@
@Test
public void testHandleClick_projectionOnly() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaProjectionInfo.class);
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -344,31 +342,40 @@
@Test
public void testUpdateState_projectionOnly() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaProjectionInfo.class);
- device.name = "Test Projection Device";
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ "Test Projection Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
enableWifiAndProcessMessages();
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
- assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(device.name));
+ assertTrue(mCastTile.getState().secondaryLabel.toString()
+ .startsWith("Test Projection Device"));
}
@Test
public void testUpdateState_castingAndProjection() {
createAndStartTileOldImpl();
- CastController.CastDevice casting = new CastController.CastDevice();
- casting.state = CastDevice.STATE_CONNECTED;
- casting.tag = mock(RouteInfo.class);
- casting.name = "Test Casting Device";
-
- CastController.CastDevice projection = new CastController.CastDevice();
- projection.state = CastDevice.STATE_CONNECTED;
- projection.tag = mock(MediaProjectionInfo.class);
- projection.name = "Test Projection Device";
+ CastDevice casting = new CastDevice(
+ "id1",
+ /* name= */ "Test Casting Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
+ CastDevice projection = new CastDevice(
+ "id2",
+ /* name= */ "Test Projection Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(casting);
@@ -379,22 +386,27 @@
// Note here that the tile should be active, and should choose casting over projection.
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
- assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(casting.name));
+ assertTrue(mCastTile.getState().secondaryLabel.toString()
+ .startsWith("Test Casting Device"));
}
@Test
public void testUpdateState_connectedAndConnecting() {
createAndStartTileOldImpl();
- CastController.CastDevice connecting = new CastController.CastDevice();
- connecting.state = CastDevice.STATE_CONNECTING;
- connecting.tag = mock(RouteInfo.class);
- connecting.name = "Test Casting Device";
-
- CastController.CastDevice connected = new CastController.CastDevice();
- connected.state = CastDevice.STATE_CONNECTED;
- connected.tag = mock(RouteInfo.class);
- connected.name = "Test Casting Device";
-
+ CastDevice connecting = new CastDevice(
+ "id",
+ /* name= */ "Test Connecting Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connecting,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
+ CastDevice connected = new CastDevice(
+ "id",
+ /* name= */ "Test Connected Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(connecting);
devices.add(connected);
@@ -404,7 +416,8 @@
// Tile should be connected and always prefer the connected device.
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
- assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
+ assertTrue(mCastTile.getState().secondaryLabel.toString()
+ .startsWith("Test Connected Device"));
}
@Test
@@ -427,8 +440,7 @@
@Test
public void testExpandView_casting_projection() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice device = createConnectedCastDevice();
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -441,9 +453,14 @@
@Test
public void testExpandView_connecting_projection() {
createAndStartTileOldImpl();
- CastController.CastDevice connecting = new CastController.CastDevice();
- connecting.state = CastDevice.STATE_CONNECTING;
- connecting.name = "Test Casting Device";
+ CastDevice connecting = new CastDevice(
+ "id",
+ /* name= */
+ "Test Projection Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ mock(MediaProjectionInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(connecting);
@@ -457,9 +474,14 @@
@Test
public void testExpandView_casting_mediaRoute() {
createAndStartTileOldImpl();
- CastController.CastDevice device = new CastController.CastDevice();
- device.state = CastDevice.STATE_CONNECTED;
- device.tag = mock(MediaRouter.RouteInfo.class);
+ CastDevice device = new CastDevice(
+ "id",
+ /* name= */ "Test Router Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
+
List<CastDevice> devices = new ArrayList<>();
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
@@ -472,11 +494,13 @@
@Test
public void testExpandView_connecting_mediaRoute() {
createAndStartTileOldImpl();
- CastController.CastDevice connecting = new CastController.CastDevice();
- connecting.state = CastDevice.STATE_CONNECTING;
- connecting.tag = mock(RouteInfo.class);
- connecting.name = "Test Casting Device";
-
+ CastDevice connecting = new CastDevice(
+ "id",
+ /* name= */ "Test Router Device",
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connecting,
+ /* origin= */ CastDevice.CastOrigin.MediaRouter,
+ /* tag= */ mock(RouteInfo.class));
List<CastDevice> devices = new ArrayList<>();
devices.add(connecting);
when(mController.getCastDevices()).thenReturn(devices);
@@ -567,4 +591,14 @@
hotspotCallbackArgumentCaptor.capture());
mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
}
+
+ private CastDevice createConnectedCastDevice() {
+ return new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index 0683321..79cb51a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -20,6 +20,7 @@
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.systemui.res.R
@@ -54,12 +55,14 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
class InternetTileNewImplTest : SysuiTestCase() {
lateinit var underTest: InternetTileNewImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 503c52f..ce1a885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.recordissue
import android.app.Dialog
-import android.content.SharedPreferences
import android.os.UserHandle
import android.testing.TestableLooper
import android.widget.Button
@@ -57,6 +56,7 @@
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -71,7 +71,6 @@
@Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var state: IssueRecordingState
- @Mock private lateinit var sharedPreferences: SharedPreferences
@Mock
private lateinit var screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate
@Mock private lateinit var screenCaptureDisabledDialog: SystemUIDialog
@@ -192,7 +191,7 @@
anyInt(),
eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
)
- verify(factory).create(any<ScreenCapturePermissionDialogDelegate>())
+ verify(factory, times(2)).create(any(SystemUIDialog.Delegate::class.java))
}
@Test
@@ -213,7 +212,7 @@
anyInt(),
eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
)
- verify(factory, never()).create(any<ScreenCapturePermissionDialogDelegate>())
+ verify(factory, never()).create()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 0d7a9e4..f866f74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -35,8 +35,8 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -60,7 +60,7 @@
import java.io.IOException;
import java.util.concurrent.Executor;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class RecordingServiceTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index 9432451..db607fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -18,10 +18,10 @@
import android.content.Intent
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.widget.Spinner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Dependency
import com.android.systemui.SysuiTestCase
@@ -55,7 +55,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class ScreenRecordPermissionDialogDelegateTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt
new file mode 100644
index 0000000..9331c8d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/model/ScreenRecordModelTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.screenrecord.data.model
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel.Starting.Companion.toCountdownSeconds
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+
+@SmallTest
+class ScreenRecordModelTest : SysuiTestCase() {
+ @Test
+ fun countdownSeconds_millis0_is0() {
+ assertThat(0L.toCountdownSeconds()).isEqualTo(0)
+ assertThat(ScreenRecordModel.Starting(0L).countdownSeconds).isEqualTo(0)
+ }
+
+ @Test
+ fun countdownSeconds_millis500_isOne() {
+ assertThat(500L.toCountdownSeconds()).isEqualTo(1)
+ assertThat(ScreenRecordModel.Starting(500L).countdownSeconds).isEqualTo(1)
+ }
+
+ @Test
+ fun countdownSeconds_millis999_isOne() {
+ assertThat(999L.toCountdownSeconds()).isEqualTo(1)
+ assertThat(ScreenRecordModel.Starting(999L).countdownSeconds).isEqualTo(1)
+ }
+
+ @Test
+ fun countdownSeconds_millis1000_isOne() {
+ assertThat(1000L.toCountdownSeconds()).isEqualTo(1)
+ assertThat(ScreenRecordModel.Starting(1000L).countdownSeconds).isEqualTo(1)
+ }
+
+ @Test
+ fun countdownSeconds_millis1499_isOne() {
+ assertThat(1499L.toCountdownSeconds()).isEqualTo(1)
+ assertThat(ScreenRecordModel.Starting(1499L).countdownSeconds).isEqualTo(1)
+ }
+
+ @Test
+ fun countdownSeconds_millis1500_isTwo() {
+ assertThat(1500L.toCountdownSeconds()).isEqualTo(2)
+ assertThat(ScreenRecordModel.Starting(1500L).countdownSeconds).isEqualTo(2)
+ }
+
+ @Test
+ fun countdownSeconds_millis1999_isTwo() {
+ assertThat(1599L.toCountdownSeconds()).isEqualTo(2)
+ assertThat(ScreenRecordModel.Starting(1599L).countdownSeconds).isEqualTo(2)
+ }
+
+ @Test
+ fun countdownSeconds_millis2000_isTwo() {
+ assertThat(2000L.toCountdownSeconds()).isEqualTo(2)
+ assertThat(ScreenRecordModel.Starting(2000L).countdownSeconds).isEqualTo(2)
+ }
+
+ @Test
+ fun countdownSeconds_millis2500_isThree() {
+ assertThat(2500L.toCountdownSeconds()).isEqualTo(3)
+ assertThat(ScreenRecordModel.Starting(2500L).countdownSeconds).isEqualTo(3)
+ }
+
+ @Test
+ fun countdownSeconds_millis2999_isThree() {
+ assertThat(2999L.toCountdownSeconds()).isEqualTo(3)
+ assertThat(ScreenRecordModel.Starting(2999L).countdownSeconds).isEqualTo(3)
+ }
+
+ @Test
+ fun countdownSeconds_millis3000_isThree() {
+ assertThat(3000L.toCountdownSeconds()).isEqualTo(3)
+ assertThat(ScreenRecordModel.Starting(3000L).countdownSeconds).isEqualTo(3)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index 61ea437..aceea90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.screenrecord.data.repository
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -28,6 +29,7 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
@@ -35,6 +37,7 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class ScreenRecordRepositoryTest : SysuiTestCase() {
private val kosmos = Kosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
index 36639d4..67eaf7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.content.Intent
import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.truth.content.IntentSubject.assertThat as assertThatIntent
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
@@ -29,9 +30,11 @@
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.`when` as whenever
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ActionIntentCreatorTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
index 5cd3f66..612d646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -17,9 +17,9 @@
package com.android.systemui.screenshot
import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import android.os.Process.myUserHandle
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableContext
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
@@ -36,7 +36,7 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ActionIntentExecutorTest : SysuiTestCase() {
private val scheduler = TestCoroutineScheduler()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 6f5c56e..148a2e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -24,6 +24,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlin.test.Test
@@ -60,9 +61,9 @@
fun previewActionAccessed_beforeScreenshotCompleted_doesNothing() {
actionsProvider = createActionsProvider()
- val previewActionCaptor = argumentCaptor<() -> Unit>()
+ val previewActionCaptor = argumentCaptor<PreviewAction>()
verify(actionsCallback).providePreviewAction(previewActionCaptor.capture())
- previewActionCaptor.firstValue.invoke()
+ previewActionCaptor.firstValue.onClick.invoke()
verifyNoMoreInteractions(actionExecutor)
}
@@ -102,14 +103,14 @@
fun actionAccessed_whilePending_launchesMostRecentAction() = runTest {
actionsProvider = createActionsProvider()
- val previewActionCaptor = argumentCaptor<() -> Unit>()
+ val previewActionCaptor = argumentCaptor<PreviewAction>()
verify(actionsCallback).providePreviewAction(previewActionCaptor.capture())
val actionButtonCaptor = argumentCaptor<() -> Unit>()
verify(actionsCallback, times(2))
.provideActionButton(any(), any(), actionButtonCaptor.capture())
actionButtonCaptor.firstValue.invoke()
- previewActionCaptor.firstValue.invoke()
+ previewActionCaptor.firstValue.onClick.invoke()
actionButtonCaptor.secondValue.invoke()
actionsProvider.setCompletedScreenshot(validResult)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
index c6ce51a..ba330f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DraggableConstraintLayoutTest.java
@@ -20,10 +20,10 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class DraggableConstraintLayoutTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
index 924b872..b31d21e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -4,12 +4,12 @@
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.Guideline
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.screenshot.message.LabeledIcon
@@ -31,7 +31,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MessageContainerControllerTest : SysuiTestCase() {
lateinit var messageContainer: MessageContainerController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
index 2a3c31a..29e7a8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotActionsControllerTest.kt
@@ -16,9 +16,10 @@
package com.android.systemui.screenshot
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import java.util.UUID
import kotlin.test.Test
@@ -29,13 +30,13 @@
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ScreenshotActionsControllerTest : SysuiTestCase() {
private val screenshotData = mock<ScreenshotData>()
private val actionExecutor = mock<ActionExecutor>()
private val viewModel = mock<ScreenshotViewModel>()
- private val onClick = mock<() -> Unit>()
+ private val previewAction = PreviewAction("description", onClick = {})
private lateinit var actionsController: ScreenshotActionsController
private lateinit var fakeActionsProvider1: FakeActionsProvider
@@ -43,6 +44,7 @@
private val actionsProviderFactory =
object : ScreenshotActionsProvider.Factory {
var isFirstCall = true
+
override fun create(
requestId: UUID,
request: ScreenshotData,
@@ -69,16 +71,16 @@
@Test
fun setPreview_onCurrentScreenshot_updatesViewModel() {
actionsController.setCurrentScreenshot(screenshotData)
- fakeActionsProvider1.callPreview(onClick)
+ fakeActionsProvider1.callPreview(previewAction)
- verify(viewModel).setPreviewAction(onClick)
+ verify(viewModel).setPreviewAction(previewAction)
}
@Test
fun setPreview_onNonCurrentScreenshot_doesNotUpdateViewModel() {
actionsController.setCurrentScreenshot(screenshotData)
actionsController.setCurrentScreenshot(screenshotData)
- fakeActionsProvider1.callPreview(onClick)
+ fakeActionsProvider1.callPreview(previewAction)
verify(viewModel, never()).setPreviewAction(any())
}
@@ -87,8 +89,8 @@
private val actionsCallback: ScreenshotActionsController.ActionsCallback
) : ScreenshotActionsProvider {
- fun callPreview(onClick: () -> Unit) {
- actionsCallback.providePreviewAction(onClick)
+ fun callPreview(previewAction: PreviewAction) {
+ actionsCallback.providePreviewAction(previewAction)
}
override fun onScrollChipReady(onClick: Runnable) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
index f8a8a68..3ed0977 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot
import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
import android.graphics.Insets
import android.graphics.Rect
import android.os.UserHandle
@@ -25,7 +26,9 @@
import com.android.internal.util.ScreenshotRequest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
+@RunWith(AndroidJUnit4::class)
class ScreenshotDataTest {
private val type = WindowManager.TAKE_SCREENSHOT_FULLSCREEN
private val source = WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 2a9aca7..89c13b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -34,8 +34,8 @@
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -50,7 +50,7 @@
import java.util.concurrent.TimeUnit;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
/**
* Tests for exception handling and bitmap configuration in adding smart actions to Screenshot
* Notification.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
index 92c2404..5987e74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot
import android.media.MediaPlayer
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
@@ -29,11 +30,13 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class ScreenshotSoundControllerTest : SysuiTestCase() {
private val soundProvider = mock<ScreenshotSoundProvider>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
index 83c9497..471fdc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -28,8 +28,8 @@
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class SmartActionsReceiverTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index dd6ba90..0b81b5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -3,9 +3,6 @@
import android.content.ComponentName
import android.graphics.Bitmap
import android.net.Uri
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.Display.TYPE_EXTERNAL
import android.view.Display.TYPE_INTERNAL
@@ -14,10 +11,10 @@
import android.view.Display.TYPE_WIFI
import android.view.WindowManager
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.ScreenshotRequest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.display.data.repository.display
@@ -25,7 +22,6 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.kotlinArgumentCaptor as ArgumentCaptor
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.lang.IllegalStateException
@@ -42,12 +38,11 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class TakeScreenshotExecutorTest : SysuiTestCase() {
- private val controller0 = mock<ScreenshotController>()
- private val controller1 = mock<ScreenshotController>()
+ private val controller = mock<ScreenshotController>()
private val notificationsController0 = mock<ScreenshotNotificationsController>()
private val notificationsController1 = mock<ScreenshotNotificationsController>()
private val controllerFactory = mock<ScreenshotController.Factory>()
@@ -59,6 +54,7 @@
private val topComponent = ComponentName(mContext, TakeScreenshotExecutorTest::class.java)
private val testScope = TestScope(UnconfinedTestDispatcher())
private val eventLogger = UiEventLoggerFake()
+ private val headlessHandler = mock<HeadlessScreenshotHandler>()
private val screenshotExecutor =
TakeScreenshotExecutorImpl(
@@ -67,20 +63,18 @@
testScope,
requestProcessor,
eventLogger,
- notificationControllerFactory
+ notificationControllerFactory,
+ headlessHandler,
)
@Before
fun setUp() {
- whenever(controllerFactory.create(any(), any())).thenAnswer {
- if (it.getArgument<Display>(0).displayId == 0) controller0 else controller1
- }
+ whenever(controllerFactory.create(any(), any())).thenReturn(controller)
whenever(notificationControllerFactory.create(eq(0))).thenReturn(notificationsController0)
whenever(notificationControllerFactory.create(eq(1))).thenReturn(notificationsController1)
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_severalDisplays_callsControllerForEachOne() =
testScope.runTest {
val internalDisplay = display(TYPE_INTERNAL, id = 0)
@@ -90,14 +84,14 @@
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
verify(controllerFactory).create(eq(internalDisplay), any())
- verify(controllerFactory).create(eq(externalDisplay), any())
+ verify(controllerFactory, never()).create(eq(externalDisplay), any())
val capturer = ArgumentCaptor<ScreenshotData>()
- verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ verify(controller).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value.displayId).isEqualTo(0)
// OnSaved callback should be different.
- verify(controller1).handleScreenshot(capturer.capture(), any(), any())
+ verify(headlessHandler).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value.displayId).isEqualTo(1)
assertThat(eventLogger.numLogs()).isEqualTo(2)
@@ -112,32 +106,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
- fun executeScreenshots_severalDisplaysShelfUi_justCallsOne() =
- testScope.runTest {
- val internalDisplay = display(TYPE_INTERNAL, id = 0)
- val externalDisplay = display(TYPE_EXTERNAL, id = 1)
- setDisplays(internalDisplay, externalDisplay)
- val onSaved = { _: Uri? -> }
- screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
-
- verify(controllerFactory).create(eq(internalDisplay), any())
-
- val capturer = ArgumentCaptor<ScreenshotData>()
-
- verify(controller0).handleScreenshot(capturer.capture(), any(), any())
- assertThat(capturer.value.displayId).isEqualTo(0)
-
- assertThat(eventLogger.numLogs()).isEqualTo(1)
- assertThat(eventLogger.get(0).eventId)
- .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
- assertThat(eventLogger.get(0).packageName).isEqualTo(topComponent.packageName)
-
- screenshotExecutor.onDestroy()
- }
-
- @Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_providedImageType_callsOnlyDefaultDisplayController() =
testScope.runTest {
val internalDisplay = display(TYPE_INTERNAL, id = 0)
@@ -155,10 +123,10 @@
val capturer = ArgumentCaptor<ScreenshotData>()
- verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ verify(controller).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value.displayId).isEqualTo(0)
// OnSaved callback should be different.
- verify(controller1, never()).handleScreenshot(any(), any(), any())
+ verify(headlessHandler, never()).handleScreenshot(any(), any(), any())
assertThat(eventLogger.numLogs()).isEqualTo(1)
assertThat(eventLogger.get(0).eventId)
@@ -169,7 +137,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_onlyVirtualDisplays_noInteractionsWithControllers() =
testScope.runTest {
setDisplays(display(TYPE_VIRTUAL, id = 0), display(TYPE_VIRTUAL, id = 1))
@@ -177,14 +144,14 @@
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
verifyNoMoreInteractions(controllerFactory)
+ verify(headlessHandler, never()).handleScreenshot(any(), any(), any())
screenshotExecutor.onDestroy()
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_allowedTypes_allCaptured() =
testScope.runTest {
- whenever(controllerFactory.create(any(), any())).thenReturn(controller0)
+ whenever(controllerFactory.create(any(), any())).thenReturn(controller)
setDisplays(
display(TYPE_INTERNAL, id = 0),
@@ -195,12 +162,12 @@
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
- verify(controller0, times(4)).handleScreenshot(any(), any(), any())
+ verify(controller, times(1)).handleScreenshot(any(), any(), any())
+ verify(headlessHandler, times(3)).handleScreenshot(any(), any(), any())
screenshotExecutor.onDestroy()
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_reportsOnFinishedOnlyWhenBothFinished() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -210,8 +177,8 @@
val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
- verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
- verify(controller1).handleScreenshot(any(), any(), capturer1.capture())
+ verify(controller).handleScreenshot(any(), any(), capturer0.capture())
+ verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
verify(callback, never()).onFinish()
@@ -226,7 +193,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_oneFinishesOtherFails_reportFailsOnlyAtTheEnd() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -236,8 +202,8 @@
val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
- verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
- verify(controller1).handleScreenshot(any(), nullable(), capturer1.capture())
+ verify(controller).handleScreenshot(any(), any(), capturer0.capture())
+ verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
verify(callback, never()).onFinish()
@@ -254,7 +220,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_allDisplaysFail_reportsFail() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -264,8 +229,8 @@
val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
- verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
- verify(controller1).handleScreenshot(any(), any(), capturer1.capture())
+ verify(controller).handleScreenshot(any(), any(), capturer0.capture())
+ verify(headlessHandler).handleScreenshot(any(), any(), capturer1.capture())
verify(callback, never()).onFinish()
@@ -282,7 +247,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun onDestroy_propagatedToControllers() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -290,59 +254,50 @@
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onDestroy()
- verify(controller0).onDestroy()
- verify(controller1).onDestroy()
+ verify(controller).onDestroy()
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
- fun removeWindows_propagatedToControllers() =
+ fun removeWindows_propagatedToController() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.removeWindows()
- verify(controller0).removeWindow()
- verify(controller1).removeWindow()
+ verify(controller).removeWindow()
screenshotExecutor.onDestroy()
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
- fun onCloseSystemDialogsReceived_propagatedToControllers() =
+ fun onCloseSystemDialogsReceived_propagatedToController() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onCloseSystemDialogsReceived()
- verify(controller0).requestDismissal(any())
- verify(controller1).requestDismissal(any())
+ verify(controller).requestDismissal(any())
screenshotExecutor.onDestroy()
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
- fun onCloseSystemDialogsReceived_someControllerHavePendingTransitions() =
+ fun onCloseSystemDialogsReceived_controllerHasPendingTransitions() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
- whenever(controller0.isPendingSharedTransition).thenReturn(true)
- whenever(controller1.isPendingSharedTransition).thenReturn(false)
+ whenever(controller.isPendingSharedTransition).thenReturn(true)
val onSaved = { _: Uri? -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
screenshotExecutor.onCloseSystemDialogsReceived()
- verify(controller0, never()).requestDismissal(any())
- verify(controller1).requestDismissal(any())
+ verify(controller, never()).requestDismissal(any())
screenshotExecutor.onDestroy()
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_controllerCalledWithRequestProcessorReturnValue() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0))
@@ -357,14 +312,13 @@
.isEqualTo(ScreenshotData.fromRequest(screenshotRequest))
val capturer = ArgumentCaptor<ScreenshotData>()
- verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ verify(controller).handleScreenshot(capturer.capture(), any(), any())
assertThat(capturer.value).isEqualTo(toBeReturnedByProcessor)
screenshotExecutor.onDestroy()
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_errorFromProcessor_logsScreenshotRequested() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -382,7 +336,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_errorFromProcessor_logsUiError() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -400,7 +353,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_errorFromProcessorOnDefaultDisplay_showsErrorNotification() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -427,14 +379,13 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_errorFromScreenshotController_reportsRequested() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
- whenever(controller0.handleScreenshot(any(), any(), any()))
+ whenever(controller.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
- whenever(controller1.handleScreenshot(any(), any(), any()))
+ whenever(headlessHandler.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
@@ -448,14 +399,13 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_errorFromScreenshotController_reportsError() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
- whenever(controller0.handleScreenshot(any(), any(), any()))
+ whenever(controller.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
- whenever(controller1.handleScreenshot(any(), any(), any()))
+ whenever(headlessHandler.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
@@ -469,14 +419,13 @@
}
@Test
- @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
fun executeScreenshots_errorFromScreenshotController_showsErrorNotification() =
testScope.runTest {
setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
val onSaved = { _: Uri? -> }
- whenever(controller0.handleScreenshot(any(), any(), any()))
+ whenever(controller.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
- whenever(controller1.handleScreenshot(any(), any(), any()))
+ whenever(headlessHandler.handleScreenshot(any(), any(), any()))
.thenThrow(IllegalStateException::class.java)
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
@@ -495,7 +444,7 @@
assertThat(it).isNull()
onSavedCallCount += 1
}
- whenever(controller0.handleScreenshot(any(), any(), any())).thenAnswer {
+ whenever(controller.handleScreenshot(any(), any(), any())).thenAnswer {
(it.getArgument(1) as Consumer<Uri?>).accept(null)
}
@@ -524,6 +473,7 @@
var processed: ScreenshotData? = null
var toReturn: ScreenshotData? = null
var shouldThrowException = false
+
override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
if (shouldThrowException) throw RequestProcessorException("")
processed = screenshot
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
index ebdc49f..7d2d7c45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
@@ -30,12 +30,12 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;
-import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -54,7 +54,7 @@
import java.util.concurrent.TimeUnit;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class WorkProfileMessageControllerTest extends SysuiTestCase {
private static final String FILES_APP_COMPONENT = "com.android.test/.FilesComponent";
private static final String FILES_APP_LABEL = "Custom Files App";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index 68a6893..9986205 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot.appclips;
import static android.app.Activity.RESULT_OK;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
@@ -25,30 +26,48 @@
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityTaskManager.RootTaskInfo;
+import android.app.IActivityTaskManager;
+import android.app.assist.AssistContent;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.view.Display;
+import android.view.View;
+import android.widget.CheckBox;
import android.widget.ImageView;
+import android.widget.TextView;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.intercepting.SingleActivityFactory;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
+import com.android.systemui.screenshot.AssistContentRequester;
import com.android.systemui.screenshot.ImageExporter;
import com.android.systemui.settings.UserTracker;
@@ -60,9 +79,11 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
@@ -75,14 +96,30 @@
private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
private static final String TEST_URI_STRING = "www.test-uri.com";
private static final Uri TEST_URI = Uri.parse(TEST_URI_STRING);
- private static final BiConsumer<Integer, Bundle> FAKE_CONSUMER = (unUsed1, unUsed2) -> {};
+ private static final BiConsumer<Integer, Bundle> FAKE_CONSUMER = (unUsed1, unUsed2) -> {
+ };
private static final String TEST_CALLING_PACKAGE = "test-calling-package";
+ private static final int BACKLINKS_TASK_ID = 42;
+ private static final String BACKLINKS_TASK_APP_NAME = "Backlinks app";
+ private static final String BACKLINKS_TASK_PACKAGE_NAME = "backlinksTaskPackageName";
+
+ private static final RootTaskInfo TASK_THAT_SUPPORTS_BACKLINKS =
+ createTaskInfoForBacklinksTask();
+ private static final AssistContent ASSIST_CONTENT_FOR_BACKLINKS_TASK =
+ createAssistContentForBacklinksTask();
+ private static final Drawable FAKE_DRAWABLE = new ShapeDrawable();
+
+ private ArgumentCaptor<Integer> mDisplayIdCaptor = ArgumentCaptor.forClass(Integer.class);
@Mock
private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
@Mock
private ImageExporter mImageExporter;
@Mock
+ private IActivityTaskManager mAtmService;
+ @Mock
+ private AssistContentRequester mAssistContentRequester;
+ @Mock
private PackageManager mPackageManager;
@Mock
private UserTracker mUserTracker;
@@ -98,9 +135,10 @@
protected AppClipsActivityTestable create(Intent unUsed) {
return new AppClipsActivityTestable(
new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper,
- mImageExporter, getContext().getMainExecutor(),
- directExecutor()), mPackageManager, mUserTracker,
- mUiEventLogger);
+ mImageExporter, mAtmService, mAssistContentRequester,
+ mPackageManager, getContext().getMainExecutor(),
+ directExecutor()),
+ mPackageManager, mUserTracker, mUiEventLogger);
}
};
@@ -118,7 +156,7 @@
when(mPackageManager.getApplicationInfoAsUser(eq(TEST_CALLING_PACKAGE),
any(ApplicationInfoFlags.class), eq(TEST_USER_ID))).thenReturn(applicationInfo);
- when(mAppClipsCrossProcessHelper.takeScreenshot()).thenReturn(TEST_BITMAP);
+ when(mAppClipsCrossProcessHelper.takeScreenshot(anyInt())).thenReturn(TEST_BITMAP);
ImageExporter.Result result = new ImageExporter.Result();
result.uri = TEST_URI;
when(mImageExporter.export(any(Executor.class), any(UUID.class), any(Bitmap.class),
@@ -132,10 +170,15 @@
}
@Test
+ @DisableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
public void appClipsLaunched_screenshotDisplayed() {
launchActivity();
assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull();
+ assertThat(mActivity.findViewById(R.id.backlinks_data).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(mActivity.findViewById(R.id.backlinks_include_data).getVisibility())
+ .isEqualTo(View.GONE);
}
@Test
@@ -176,6 +219,59 @@
verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_CANCELLED, TEST_UID, TEST_CALLING_PACKAGE);
}
+ @Test
+ @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+ public void appClipsLaunched_backlinks_displayed() throws RemoteException {
+ setUpMocksForBacklinks();
+
+ launchActivity();
+ waitForIdleSync();
+
+ assertThat(mDisplayIdCaptor.getValue()).isEqualTo(mActivity.getDisplayId());
+ TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
+ assertThat(backlinksData.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(backlinksData.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+ assertThat(backlinksData.getCompoundDrawablesRelative()[0]).isEqualTo(FAKE_DRAWABLE);
+
+ CheckBox backlinksIncludeData = mActivity.findViewById(R.id.backlinks_include_data);
+ assertThat(backlinksIncludeData.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(backlinksIncludeData.getText().toString())
+ .isEqualTo(mActivity.getString(R.string.backlinks_include_link));
+ assertThat(backlinksIncludeData.isChecked()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+ public void appClipsLaunched_backlinks_doNotIncludeLink() throws RemoteException {
+ setUpMocksForBacklinks();
+
+ launchActivity();
+ waitForIdleSync();
+ CheckBox backlinksIncludeData = mActivity.findViewById(R.id.backlinks_include_data);
+ runOnMainThread(() -> backlinksIncludeData.performClick());
+ waitForIdleSync();
+
+ assertThat(backlinksIncludeData.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(backlinksIncludeData.isChecked()).isFalse();
+
+ TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
+ assertThat(backlinksData.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ private void setUpMocksForBacklinks() throws RemoteException {
+ when(mAtmService.getAllRootTaskInfosOnDisplay(mDisplayIdCaptor.capture()))
+ .thenReturn(List.of(TASK_THAT_SUPPORTS_BACKLINKS));
+ doAnswer(invocation -> {
+ AssistContentRequester.Callback callback = invocation.getArgument(1);
+ callback.onAssistContentAvailable(ASSIST_CONTENT_FOR_BACKLINKS_TASK);
+ return null;
+ }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+ when(mPackageManager
+ .resolveActivity(any(Intent.class), anyInt()))
+ .thenReturn(createBacklinksTaskResolveInfo());
+ when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+ }
+
private void launchActivity() {
launchActivity(createResultReceiver(FAKE_CONSUMER));
}
@@ -203,7 +299,7 @@
testReceiver.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
- testReceiver = ResultReceiver.CREATOR.createFromParcel(parcel);
+ testReceiver = ResultReceiver.CREATOR.createFromParcel(parcel);
parcel.recycle();
return testReceiver;
}
@@ -212,6 +308,37 @@
mContext.getMainExecutor().execute(runnable);
}
+ private static ResolveInfo createBacklinksTaskResolveInfo() {
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.applicationInfo = new ApplicationInfo();
+ activityInfo.name = BACKLINKS_TASK_APP_NAME;
+ activityInfo.packageName = BACKLINKS_TASK_PACKAGE_NAME;
+ activityInfo.applicationInfo.packageName = BACKLINKS_TASK_PACKAGE_NAME;
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = activityInfo;
+ return resolveInfo;
+ }
+
+ private static RootTaskInfo createTaskInfoForBacklinksTask() {
+ RootTaskInfo taskInfo = new RootTaskInfo();
+ taskInfo.taskId = BACKLINKS_TASK_ID;
+ taskInfo.isVisible = true;
+ taskInfo.isRunning = true;
+ taskInfo.numActivities = 1;
+ taskInfo.topActivity = new ComponentName(BACKLINKS_TASK_PACKAGE_NAME, "backlinksClass");
+ taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
+ taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
+ taskInfo.childTaskIds = new int[]{BACKLINKS_TASK_ID + 1};
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ return taskInfo;
+ }
+
+ private static AssistContent createAssistContentForBacklinksTask() {
+ AssistContent content = new AssistContent();
+ content.setWebUri(Uri.parse("https://developers.android.com"));
+ return content;
+ }
+
public static class AppClipsActivityTestable extends AppClipsActivity {
public AppClipsActivityTestable(AppClipsViewModel.Factory viewModelFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
index ad0797c..baf1357 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
@@ -16,28 +16,51 @@
package com.android.systemui.screenshot.appclips;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT;
+import static android.content.ClipDescription.MIMETYPE_TEXT_URILIST;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.ACTION_VIEW;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityTaskManager.RootTaskInfo;
+import android.app.IActivityTaskManager;
+import android.app.assist.AssistContent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.net.Uri;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
-import android.view.Display;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.screenshot.AssistContentRequester;
import com.android.systemui.screenshot.ImageExporter;
import com.google.common.util.concurrent.Futures;
@@ -45,9 +68,13 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -60,27 +87,45 @@
private static final Rect FAKE_RECT = new Rect();
private static final Uri FAKE_URI = Uri.parse("www.test-uri.com");
private static final UserHandle USER_HANDLE = Process.myUserHandle();
+ private static final int BACKLINKS_TASK_ID = 42;
+ private static final String BACKLINKS_TASK_APP_NAME = "Ultimate question app";
+ private static final String BACKLINKS_TASK_PACKAGE_NAME = "backlinksTaskPackageName";
+ private static final AssistContent EMPTY_ASSIST_CONTENT = new AssistContent();
@Mock private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
@Mock private ImageExporter mImageExporter;
+ @Mock private IActivityTaskManager mAtmService;
+ @Mock private AssistContentRequester mAssistContentRequester;
+ @Mock
+ private PackageManager mPackageManager;
+ private ArgumentCaptor<Intent> mPackageManagerIntentCaptor;
private AppClipsViewModel mViewModel;
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
+ mPackageManagerIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ // Set up mocking for backlinks.
+ when(mAtmService.getAllRootTaskInfosOnDisplay(DEFAULT_DISPLAY))
+ .thenReturn(List.of(createTaskInfoForBacklinksTask()));
+ when(mPackageManager.resolveActivity(mPackageManagerIntentCaptor.capture(), anyInt()))
+ .thenReturn(createBacklinksTaskResolveInfo());
+ when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
mViewModel = new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper, mImageExporter,
+ mAtmService, mAssistContentRequester, mPackageManager,
getContext().getMainExecutor(), directExecutor()).create(AppClipsViewModel.class);
}
@Test
public void performScreenshot_fails_shouldUpdateErrorWithFailed() {
- when(mAppClipsCrossProcessHelper.takeScreenshot()).thenReturn(null);
+ when(mAppClipsCrossProcessHelper.takeScreenshot(anyInt())).thenReturn(null);
- mViewModel.performScreenshot();
+ mViewModel.performScreenshot(DEFAULT_DISPLAY);
waitForIdleSync();
- verify(mAppClipsCrossProcessHelper).takeScreenshot();
+ verify(mAppClipsCrossProcessHelper).takeScreenshot(DEFAULT_DISPLAY);
assertThat(mViewModel.getErrorLiveData().getValue())
.isEqualTo(CAPTURE_CONTENT_FOR_NOTE_FAILED);
assertThat(mViewModel.getResultLiveData().getValue()).isNull();
@@ -88,12 +133,12 @@
@Test
public void performScreenshot_succeeds_shouldUpdateScreenshotWithBitmap() {
- when(mAppClipsCrossProcessHelper.takeScreenshot()).thenReturn(FAKE_BITMAP);
+ when(mAppClipsCrossProcessHelper.takeScreenshot(DEFAULT_DISPLAY)).thenReturn(FAKE_BITMAP);
- mViewModel.performScreenshot();
+ mViewModel.performScreenshot(DEFAULT_DISPLAY);
waitForIdleSync();
- verify(mAppClipsCrossProcessHelper).takeScreenshot();
+ verify(mAppClipsCrossProcessHelper).takeScreenshot(DEFAULT_DISPLAY);
assertThat(mViewModel.getErrorLiveData().getValue()).isNull();
assertThat(mViewModel.getScreenshot().getValue()).isEqualTo(FAKE_BITMAP);
}
@@ -101,7 +146,7 @@
@Test
public void saveScreenshot_throwsError_shouldUpdateErrorWithFailed() {
when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), eq(USER_HANDLE),
- eq(Display.DEFAULT_DISPLAY))).thenReturn(
+ eq(DEFAULT_DISPLAY))).thenReturn(
Futures.immediateFailedFuture(new ExecutionException(new Throwable())));
mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT, USER_HANDLE);
@@ -115,7 +160,7 @@
@Test
public void saveScreenshot_failsSilently_shouldUpdateErrorWithFailed() {
when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), eq(USER_HANDLE),
- eq(Display.DEFAULT_DISPLAY))).thenReturn(
+ eq(DEFAULT_DISPLAY))).thenReturn(
Futures.immediateFuture(new ImageExporter.Result()));
mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT, USER_HANDLE);
@@ -131,7 +176,7 @@
ImageExporter.Result result = new ImageExporter.Result();
result.uri = FAKE_URI;
when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), eq(USER_HANDLE),
- eq(Display.DEFAULT_DISPLAY))).thenReturn(Futures.immediateFuture(result));
+ eq(DEFAULT_DISPLAY))).thenReturn(Futures.immediateFuture(result));
mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT, USER_HANDLE);
waitForIdleSync();
@@ -139,4 +184,206 @@
assertThat(mViewModel.getErrorLiveData().getValue()).isNull();
assertThat(mViewModel.getResultLiveData().getValue()).isEqualTo(FAKE_URI);
}
+
+ @Test
+ public void triggerBacklinks_shouldUpdateBacklinks_withUri() {
+ Uri expectedUri = Uri.parse("https://developers.android.com");
+ AssistContent contentWithUri = new AssistContent();
+ contentWithUri.setWebUri(expectedUri);
+ doAnswer(invocation -> {
+ AssistContentRequester.Callback callback = invocation.getArgument(1);
+ callback.onAssistContentAvailable(contentWithUri);
+ return null;
+ }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
+ assertThat(queriedIntent.getData()).isEqualTo(expectedUri);
+ assertThat(queriedIntent.getAction()).isEqualTo(ACTION_VIEW);
+
+ InternalBacklinksData result = mViewModel.getBacklinksLiveData().getValue();
+ assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
+ ClipData clipData = result.getClipData();
+ ClipDescription resultDescription = clipData.getDescription();
+ assertThat(resultDescription.getLabel().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+ assertThat(resultDescription.getMimeType(0)).isEqualTo(MIMETYPE_TEXT_URILIST);
+ assertThat(clipData.getItemCount()).isEqualTo(1);
+ assertThat(clipData.getItemAt(0).getUri()).isEqualTo(expectedUri);
+ }
+
+ @Test
+ public void triggerBacklinks_withNonResolvableUri_usesMainLauncherIntent() {
+ Uri expectedUri = Uri.parse("https://developers.android.com");
+ AssistContent contentWithUri = new AssistContent();
+ contentWithUri.setWebUri(expectedUri);
+ resetPackageManagerMockingForUsingFallbackBacklinks();
+ doAnswer(invocation -> {
+ AssistContentRequester.Callback callback = invocation.getArgument(1);
+ callback.onAssistContentAvailable(contentWithUri);
+ return null;
+ }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ verifyMainLauncherBacklinksIntent();
+ }
+
+ @Test
+ public void triggerBacklinks_shouldUpdateBacklinks_withAppProvidedIntent() {
+ Intent expectedIntent = new Intent().setPackage(BACKLINKS_TASK_PACKAGE_NAME);
+ AssistContent contentWithAppProvidedIntent = new AssistContent();
+ contentWithAppProvidedIntent.setIntent(expectedIntent);
+ doAnswer(invocation -> {
+ AssistContentRequester.Callback callback = invocation.getArgument(1);
+ callback.onAssistContentAvailable(contentWithAppProvidedIntent);
+ return null;
+ }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
+ assertThat(queriedIntent.getPackage()).isEqualTo(expectedIntent.getPackage());
+
+ InternalBacklinksData result = mViewModel.getBacklinksLiveData().getValue();
+ assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
+ ClipData clipData = result.getClipData();
+ ClipDescription resultDescription = clipData.getDescription();
+ assertThat(resultDescription.getLabel().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+ assertThat(resultDescription.getMimeType(0)).isEqualTo(MIMETYPE_TEXT_INTENT);
+ assertThat(clipData.getItemCount()).isEqualTo(1);
+ assertThat(clipData.getItemAt(0).getIntent()).isEqualTo(expectedIntent);
+ }
+
+ @Test
+ public void triggerBacklinks_withNonResolvableAppProvidedIntent_usesMainLauncherIntent() {
+ Intent expectedIntent = new Intent().setPackage(BACKLINKS_TASK_PACKAGE_NAME);
+ AssistContent contentWithAppProvidedIntent = new AssistContent();
+ contentWithAppProvidedIntent.setIntent(expectedIntent);
+ resetPackageManagerMockingForUsingFallbackBacklinks();
+ doAnswer(invocation -> {
+ AssistContentRequester.Callback callback = invocation.getArgument(1);
+ callback.onAssistContentAvailable(contentWithAppProvidedIntent);
+ return null;
+ }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ verifyMainLauncherBacklinksIntent();
+ }
+
+ @Test
+ public void triggerBacklinks_shouldUpdateBacklinks_withMainLauncherIntent() {
+ doAnswer(invocation -> {
+ AssistContentRequester.Callback callback = invocation.getArgument(1);
+ callback.onAssistContentAvailable(EMPTY_ASSIST_CONTENT);
+ return null;
+ }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
+ assertThat(queriedIntent.getPackage()).isEqualTo(BACKLINKS_TASK_PACKAGE_NAME);
+ assertThat(queriedIntent.getAction()).isEqualTo(ACTION_MAIN);
+ assertThat(queriedIntent.getCategories()).containsExactly(CATEGORY_LAUNCHER);
+
+ verifyMainLauncherBacklinksIntent();
+ }
+
+ @Test
+ public void triggerBacklinks_withNonResolvableMainLauncherIntent_noBacklinksAvailable() {
+ reset(mPackageManager);
+ doAnswer(invocation -> {
+ AssistContentRequester.Callback callback = invocation.getArgument(1);
+ callback.onAssistContentAvailable(EMPTY_ASSIST_CONTENT);
+ return null;
+ }).when(mAssistContentRequester).requestAssistContent(eq(BACKLINKS_TASK_ID), any());
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ assertThat(mViewModel.getBacklinksLiveData().getValue()).isNull();
+ }
+
+ @Test
+ public void triggerBacklinks_nonStandardActivityIgnored_noBacklinkAvailable()
+ throws RemoteException {
+ reset(mAtmService);
+ RootTaskInfo taskInfo = createTaskInfoForBacklinksTask();
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME);
+ when(mAtmService.getAllRootTaskInfosOnDisplay(DEFAULT_DISPLAY))
+ .thenReturn(List.of(taskInfo));
+
+ mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ assertThat(mViewModel.getBacklinksLiveData().getValue()).isNull();
+ }
+
+ @Test
+ public void triggerBacklinks_taskIdsToIgnoreConsidered_noBacklinkAvailable() {
+ mViewModel.triggerBacklinks(Set.of(BACKLINKS_TASK_ID), DEFAULT_DISPLAY);
+ waitForIdleSync();
+
+ assertThat(mViewModel.getBacklinksLiveData().getValue()).isNull();
+ }
+
+ private void resetPackageManagerMockingForUsingFallbackBacklinks() {
+ reset(mPackageManager);
+ when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+ when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+ // First the logic queries whether a package has a launcher activity, this should
+ // resolve otherwise the logic filters out the task.
+ .thenReturn(createBacklinksTaskResolveInfo())
+ // Then logic queries with the backlinks intent, this should not resolve for the
+ // logic to use the fallback intent.
+ .thenReturn(null);
+ }
+
+ private void verifyMainLauncherBacklinksIntent() {
+ InternalBacklinksData result = mViewModel.getBacklinksLiveData().getValue();
+ assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
+
+ ClipData clipData = result.getClipData();
+ assertThat(clipData.getItemCount()).isEqualTo(1);
+
+ ClipDescription resultDescription = clipData.getDescription();
+ assertThat(resultDescription.getLabel().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+ assertThat(resultDescription.getMimeType(0)).isEqualTo(MIMETYPE_TEXT_INTENT);
+
+ Intent actualBacklinksIntent = clipData.getItemAt(0).getIntent();
+ assertThat(actualBacklinksIntent.getPackage()).isEqualTo(BACKLINKS_TASK_PACKAGE_NAME);
+ assertThat(actualBacklinksIntent.getAction()).isEqualTo(ACTION_MAIN);
+ assertThat(actualBacklinksIntent.getCategories()).containsExactly(CATEGORY_LAUNCHER);
+ }
+
+ private static ResolveInfo createBacklinksTaskResolveInfo() {
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.applicationInfo = new ApplicationInfo();
+ activityInfo.name = BACKLINKS_TASK_APP_NAME;
+ activityInfo.packageName = BACKLINKS_TASK_PACKAGE_NAME;
+ activityInfo.applicationInfo.packageName = BACKLINKS_TASK_PACKAGE_NAME;
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = activityInfo;
+ return resolveInfo;
+ }
+
+ private static RootTaskInfo createTaskInfoForBacklinksTask() {
+ RootTaskInfo taskInfo = new RootTaskInfo();
+ taskInfo.taskId = BACKLINKS_TASK_ID;
+ taskInfo.isVisible = true;
+ taskInfo.isRunning = true;
+ taskInfo.numActivities = 1;
+ taskInfo.topActivity = new ComponentName(BACKLINKS_TASK_PACKAGE_NAME, "backlinksClass");
+ taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
+ taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
+ taskInfo.childTaskIds = new int[]{BACKLINKS_TASK_ID + 1};
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ return taskInfo;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
index ce2facd..6b9865f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/message/ProfileMessageControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot.message
import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
import android.content.pm.PackageManager
import android.graphics.Canvas
import android.graphics.ColorFilter
@@ -27,7 +28,9 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
+@RunWith(AndroidJUnit4::class)
class ProfileMessageControllerTest {
@Test
fun personalScreenshot() = runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
index 4945ace..3b0e194 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.screenshot.policy
import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
import android.graphics.Insets
import android.graphics.Rect
import android.os.UserHandle
@@ -34,7 +35,9 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Test
+import org.junit.runner.RunWith
+@RunWith(AndroidJUnit4::class)
class PolicyRequestProcessorTest {
val imageCapture = object : ImageCapture {
@@ -78,4 +81,4 @@
assertWithMessage("The topComponent of the screenshot").that(result.topComponent)
.isEqualTo(ComponentName.unflattenFromString(FILES))
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
index 9e3ae05..6e57761 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PrivateProfilePolicyTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot.policy
import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
import android.os.UserHandle
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.screenshot.data.model.DisplayContentModel
@@ -40,7 +41,9 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
+@RunWith(AndroidJUnit4::class)
class PrivateProfilePolicyTest {
private val kosmos = Kosmos()
private val policy = PrivateProfilePolicy(kosmos.profileTypeRepository)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
index 5d35528..8d217fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.screenshot.policy
import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -50,7 +51,9 @@
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
+@RunWith(AndroidJUnit4::class)
class WorkProfilePolicyTest {
@JvmField @Rule val setFlagsRule = SetFlagsRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
index aad46139..88cb00c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
@@ -23,8 +23,8 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
* client/connection.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class FakeSessionTest extends SysuiTestCase {
@Test
public void testNonEmptyResult_hasImage() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
index f39f543..f8de714 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
@@ -29,9 +29,9 @@
import static java.lang.Math.abs;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.view.ScrollCaptureResponse;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
@@ -46,7 +46,7 @@
* screenshots.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ScrollCaptureControllerTest extends SysuiTestCase {
private static final ScrollCaptureResponse EMPTY_RESPONSE =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt
index e32086b..e39e0fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModelTest.kt
@@ -17,12 +17,15 @@
package com.android.systemui.screenshot.ui.viewmodel
import android.view.accessibility.AccessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ScreenshotViewModelTest {
private val accessibilityManager: AccessibilityManager = mock(AccessibilityManager::class.java)
private val appearance = ActionButtonAppearance(null, "Label", "Description")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java
index a345f78..ca8da63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java
@@ -22,12 +22,12 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
@@ -38,7 +38,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class ScrimViewTest extends LeakCheckedTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
index 333e634..1a4749c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
@@ -1,8 +1,8 @@
package com.android.systemui.sensorprivacy
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
@@ -11,7 +11,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class SensorUseStartedActivityTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
index 2b78405..98260d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt
@@ -19,9 +19,9 @@
import android.hardware.display.DisplayManager
import android.os.Handler
import android.service.vr.IVrManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.DisplayTracker
@@ -40,7 +40,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class BrightnessControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 31acd86..a06784e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -18,11 +18,11 @@
import android.content.Intent
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
@@ -55,7 +55,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class BrightnessDialogTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index 2b3588a..c7bea59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -16,11 +16,11 @@
package com.android.systemui.shade
-import android.testing.AndroidTestingRunner
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
private lateinit var qqsConstraint: ConstraintSet
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt
index 9b2e085..8c80835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.shade
-import android.testing.AndroidTestingRunner
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
@@ -27,7 +27,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ConstraintChangeTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt
index 0abb084..05c1706 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.shade
-import android.testing.AndroidTestingRunner
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
@@ -26,7 +26,7 @@
import org.mockito.Mockito.inOrder
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ConstraintChangesTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index b8267a0..b0213a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -18,8 +18,6 @@
import android.graphics.Rect
import android.os.PowerManager
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -29,10 +27,8 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE
import com.android.systemui.SysuiTestCase
import com.android.systemui.ambient.touch.TouchHandler
import com.android.systemui.ambient.touch.TouchMonitor
@@ -56,10 +52,8 @@
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -70,8 +64,6 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.Mock
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -184,133 +176,6 @@
}
}
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalClosed_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is closed.
- goToScene(CommunalScenes.Blank)
-
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_openGesture_interceptsTouches() =
- with(kosmos) {
- testScope.runTest {
- // Communal is closed.
- goToScene(CommunalScenes.Blank)
-
- // Initial touch down is intercepted, and so are touches outside of the region,
- // until an
- // up event is received.
- assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
- assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalTransitioning_interceptsTouches() =
- with(kosmos) {
- testScope.runTest {
- // Communal is opening.
- communalRepository.setTransitionState(
- flowOf(
- ObservableTransitionState.Transition(
- fromScene = CommunalScenes.Blank,
- toScene = CommunalScenes.Communal,
- currentScene = flowOf(CommunalScenes.Blank),
- progress = flowOf(0.5f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(true)
- )
- )
- )
- testableLooper.processAllMessages()
-
- // Touch events are intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- // User activity sent to PowerManager.
- verify(powerManager).userActivity(any(), any(), any())
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalOpen_interceptsTouches() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Touch events are intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
- // User activity sent to PowerManager.
- verify(powerManager).userActivity(any(), any(), any())
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Bouncer is visible.
- fakeKeyguardBouncerRepository.setPrimaryShow(true)
- testableLooper.processAllMessages()
-
- // Touch events are not intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- // User activity is not sent to PowerManager.
- verify(powerManager, times(0)).userActivity(any(), any(), any())
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Shade shows up.
- shadeTestUtil.setQsExpansion(1.0f)
- testableLooper.processAllMessages()
-
- // Touch events are not intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
- }
-
- @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
- @Test
- fun onTouchEvent_containerViewDisposed_doesNotIntercept() =
- with(kosmos) {
- testScope.runTest {
- // Communal is open.
- goToScene(CommunalScenes.Communal)
-
- // Touch events are intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
-
- // Container view disposed.
- underTest.disposeView()
-
- // Touch events are not intercepted.
- assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
- }
- }
-
@Test
fun lifecycle_initializedAfterConstruction() =
with(kosmos) {
@@ -517,7 +382,6 @@
}
@Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
fun fullScreenSwipeGesture_doNotProcessTouchesInNotificationStack() =
with(kosmos) {
testScope.runTest {
@@ -572,9 +436,5 @@
CONTAINER_HEIGHT.toFloat() / 2,
0
)
- private val DOWN_IN_RIGHT_SWIPE_REGION_EVENT =
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, CONTAINER_WIDTH.toFloat(), 0f, 0)
- private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- private val UP_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
index b4f890b..2ac0ed0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LockscreenHostedDreamGestureListenerTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.shade
import android.os.PowerManager
-import android.testing.AndroidTestingRunner
import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
@@ -47,7 +47,7 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class LockscreenHostedDreamGestureListenerTest : SysuiTestCase() {
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var falsingCollector: FalsingCollector
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
index bff408a..abc96ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.shade
-import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -46,7 +46,7 @@
* the set of ids, which also dictact which direction to move and when, via a filter fn.
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() {
@Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index c3cedf8..e7ca091 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -111,7 +111,7 @@
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
@@ -334,7 +334,7 @@
@Mock protected PrimaryBouncerToGoneTransitionViewModel
mPrimaryBouncerToGoneTransitionViewModel;
@Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
- @Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel;
+ @Mock protected KeyguardTouchHandlingViewModel mKeyuardTouchHandlingViewModel;
@Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock protected MotionEvent mDownMotionEvent;
@Mock protected CoroutineDispatcher mMainDispatcher;
@@ -438,6 +438,7 @@
mFakeKeyguardRepository,
mKeyguardTransitionInteractor,
mPowerInteractor,
+ mShadeRepository,
new FakeUserSetupRepository(),
mock(UserSwitcherInteractor.class),
new ShadeInteractorLegacyImpl(
@@ -463,6 +464,7 @@
() -> mShadeInteractor,
() -> mKosmos.getDeviceUnlockedInteractor(),
() -> mKosmos.getSceneInteractor(),
+ () -> mKosmos.getSceneContainerOcclusionInteractor(),
() -> mKosmos.getKeyguardClockInteractor());
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
@@ -620,6 +622,7 @@
() -> mShadeInteractor,
() -> mKosmos.getDeviceUnlockedInteractor(),
() -> mKosmos.getSceneInteractor(),
+ () -> mKosmos.getSceneContainerOcclusionInteractor(),
() -> mKosmos.getKeyguardClockInteractor()),
mKeyguardBypassController,
mDozeParameters,
@@ -752,7 +755,7 @@
mMainDispatcher,
mKeyguardTransitionInteractor,
mDumpManager,
- mKeyuardLongPressViewModel,
+ mKeyuardTouchHandlingViewModel,
mKeyguardInteractor,
mActivityStarter,
mSharedNotificationContainerInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 13d44de..90e8ea5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -47,13 +47,14 @@
import android.graphics.Point;
import android.os.PowerManager;
import android.platform.test.annotations.DisableFlags;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.DejankUtils;
@@ -78,7 +79,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NotificationPanelViewControllerTest extends NotificationPanelViewControllerBaseTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index d47bd47..e1d92e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -19,11 +19,11 @@
package com.android.systemui.shade
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.ViewStub
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.CollectionUtils
import com.android.keyguard.KeyguardClockSwitch.LARGE
@@ -55,7 +55,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class NotificationPanelViewControllerWithCoroutinesTest :
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 31bd12f..fec7424 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -16,10 +16,10 @@
package com.android.systemui.shade
import android.os.SystemClock
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityContainerController
import com.android.keyguard.LegacyLockIconViewController
@@ -82,7 +82,7 @@
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class NotificationShadeWindowViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
index 0c3af03..97ce37c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
@@ -16,11 +16,11 @@
package com.android.systemui.shade
-import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSFragmentLegacy
@@ -34,7 +34,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationsQuickSettingsContainerTest : SysuiTestCase() {
@Mock private lateinit var qsFrame: View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index 3900def..effd28ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -20,9 +20,9 @@
import android.os.PowerManager
import android.provider.Settings.Secure.DOZE_DOUBLE_TAP_GESTURE
import android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dock.DockManager
@@ -49,7 +49,7 @@
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class PulsingGestureListenerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index 2cd740d..f38bf13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -3,8 +3,8 @@
import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.view.DisplayCutout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -21,7 +21,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class QsBatteryModeControllerTest : SysuiTestCase() {
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 85541aa..06a883c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -218,6 +218,7 @@
mKeyguardRepository,
keyguardTransitionInteractor,
powerInteractor,
+ mShadeRepository,
new FakeUserSetupRepository(),
mUserSwitcherInteractor,
new ShadeInteractorLegacyImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
index ad4b4fd..9a6423d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
@@ -36,10 +36,10 @@
import static org.mockito.Mockito.when;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.qs.QS;
@@ -53,7 +53,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class QuickSettingsControllerImplTest extends QuickSettingsControllerImplBaseTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
index dfd7a71..46961d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade
import android.app.StatusBarManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.google.common.truth.Truth.assertThat
@@ -25,9 +26,11 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class QuickSettingsControllerImplWithCoroutinesTest : QuickSettingsControllerImplBaseTest() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 6bdd3ef..0217238 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -16,10 +16,10 @@
package com.android.systemui.shade
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
@@ -59,7 +59,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class ShadeControllerImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt
index 89ae42f..4734ede 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeExpansionStateManagerTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.shade
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ShadeExpansionStateManagerTest : SysuiTestCase() {
private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
index 7a9ef62..c669959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/CellSignalStateTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.shade.carrier
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertNotSame
@@ -24,7 +24,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class CellSignalStateTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
index a42121a..1a2531a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
@@ -21,13 +21,13 @@
import static org.junit.Assert.assertTrue;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.mobile.TelephonyIcons;
@@ -38,7 +38,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@SmallTest
public class ShadeCarrierTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 750693c..97acc6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -40,7 +40,7 @@
@Before
fun setUp() {
- underTest = ShadeRepositoryImpl()
+ underTest = ShadeRepositoryImpl(getContext())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
index 7c33648..10c017b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
@@ -1,6 +1,6 @@
package com.android.systemui.shade.transition
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
@@ -30,7 +30,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ScrimShadeTransitionControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
index 4679a58..89a3d5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
@@ -20,8 +20,8 @@
import android.content.res.Resources
import android.content.res.TypedArray
import android.platform.test.annotations.PlatinumTest
-import android.testing.AndroidTestingRunner
import android.util.AttributeSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.R
@@ -39,7 +39,7 @@
@PlatinumTest(focusArea = "sysui")
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class DoubleShadowTextClockTest : SysuiTestCase() {
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt
index fc230e3..5d8f868 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt
@@ -17,9 +17,9 @@
package com.android.systemui.shared.animation
import android.graphics.Paint
-import android.testing.AndroidTestingRunner
import android.widget.FrameLayout
import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class DisableSubpixelTextTransitionListenerTest : SysuiTestCase() {
private lateinit var disableSubpixelTextTransitionListener:
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
index 293dc04..3a53a5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -14,9 +14,9 @@
*/
package com.android.systemui.shared.animation
-import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
@@ -34,7 +34,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
private val progressProvider = FakeUnfoldTransitionProvider()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
index 3cb48d9..fbaddfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
@@ -15,8 +15,8 @@
package com.android.systemui.shared.animation
import android.graphics.Point
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.Surface.ROTATION_0
import android.view.Surface.ROTATION_90
@@ -36,7 +36,7 @@
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
index 7b0cd19..e076418 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.shared.clocks
-import android.testing.AndroidTestingRunner
import android.view.LayoutInflater
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators
import com.android.systemui.customization.R
@@ -34,7 +34,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class AnimatableClockViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 74d0173..2f52248 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -19,7 +19,7 @@
import android.content.ContentResolver
import android.content.Context
import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
@@ -55,7 +55,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ClockRegistryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index e0e8d1f..2522ed7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -19,10 +19,10 @@
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.customization.R
@@ -53,7 +53,7 @@
private fun DefaultClockProvider.createClock(id: ClockId): DefaultClockController =
createClock(ClockSettings(id, null)) as DefaultClockController
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DefaultClockProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
index 1a0e932..8418598 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/CombinedConditionTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.shared.condition
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.condition.Condition.START_EAGERLY
@@ -32,7 +32,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class CombinedConditionTest : SysuiTestCase() {
class FakeCondition
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
index 0a8210d..83fb14a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
@@ -1,6 +1,6 @@
package com.android.systemui.shared.condition
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -19,7 +19,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ConditionExtensionsTest : SysuiTestCase() {
private lateinit var testScope: TestScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
index 65ca0a2..267f22b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
@@ -27,8 +27,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -51,7 +51,7 @@
import java.util.HashSet;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ConditionMonitorTest extends SysuiTestCase {
private FakeCondition mCondition1;
private FakeCondition mCondition2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java
index cec5d0a..a224843 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java
@@ -25,8 +25,8 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ConditionTest extends SysuiTestCase {
@Mock
CoroutineScope mScope;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
index 5fc09c7..2a6754c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
@@ -17,12 +17,12 @@
package com.android.systemui.shared.navigationbar
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import android.view.View
import android.view.ViewRootImpl
import androidx.concurrent.futures.DirectExecutor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -43,7 +43,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
class RegionSamplingHelperTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
index 0dd988d..69cc9d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepositoryTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shared.notifications.data.repository
import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -28,10 +29,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class NotificationSettingsRepositoryTest : SysuiTestCase() {
private lateinit var underTest: NotificationSettingsRepository
@@ -56,7 +56,7 @@
@Test
fun testGetIsShowNotificationsOnLockscreenEnabled() =
testScope.runTest {
- val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled)
+ val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled())
secureSettingsRepository.setInt(
name = Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
@@ -74,7 +74,7 @@
@Test
fun testSetIsShowNotificationsOnLockscreenEnabled() =
testScope.runTest {
- val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled)
+ val showNotifs by collectLastValue(underTest.isShowNotificationsOnLockScreenEnabled())
underTest.setShowNotificationsOnLockscreenEnabled(true)
assertThat(showNotifs).isEqualTo(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 40c84d6..c721ceb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -28,9 +28,9 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -55,7 +55,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class PluginManagerTest extends SysuiTestCase {
private static final String PRIVILEGED_PACKAGE = "com.android.systemui";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
index 711187b..15fdc7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -29,8 +30,10 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class VersionInfoTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
index 1031621..7fc845c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/regionsampling/RegionSamplerTest.kt
@@ -4,8 +4,8 @@
import android.app.WallpaperManager
import android.graphics.Color
import android.graphics.RectF
-import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -28,7 +28,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class RegionSamplerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
index ee3d870..5a4ef05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
@@ -15,11 +15,11 @@
*/
package com.android.systemui.shared.rotation
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.WindowInsetsController
import android.view.WindowManagerPolicyConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -27,7 +27,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
class RotationButtonControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
index b761647..d67ce30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.shared.system
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -8,6 +9,7 @@
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.only
@@ -16,6 +18,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class UncaughtExceptionPreHandlerTest : SysuiTestCase() {
private lateinit var preHandlerManager: UncaughtExceptionPreHandlerManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
index e9676c8..d0ba629 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
@@ -20,11 +20,13 @@
import android.view.CrossWindowBlurListeners
import android.view.SurfaceControl
import android.view.ViewRootImpl
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
@@ -37,6 +39,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class BlurUtilsTest : SysuiTestCase() {
@Mock lateinit var resources: Resources
@@ -117,4 +120,4 @@
return transaction
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 5da3a56..9df46e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -42,6 +42,7 @@
import android.view.WindowInsetsController.Behavior;
import android.view.accessibility.Flags;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.LetterboxDetails;
@@ -54,8 +55,10 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class CommandQueueTest extends SysuiTestCase {
private static final LetterboxDetails[] TEST_LETTERBOX_DETAILS = new LetterboxDetails[] {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
index 3b85dba..5f9c9df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -23,18 +23,21 @@
import android.content.Intent;
import android.graphics.drawable.Icon;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class NotificationUiAdjustmentTest extends SysuiTestCase {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index 9c59f9b..396d017 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -20,6 +20,7 @@
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyManager
import android.telephony.telephonyManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
@@ -44,11 +45,13 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class OperatorNameViewControllerTest : SysuiTestCase() {
private lateinit var underTest: OperatorNameViewController
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 6cac30a..1a6b420 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -68,13 +69,33 @@
}
@Test
- fun chip_inCall_isShown() =
+ fun chip_inCall_zeroStartTime_isShownAsIconOnly() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null))
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+ }
+
+ @Test
+ fun chip_inCall_negativeStartTime_isShownAsIconOnly() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = -2, intent = null))
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
+ }
+
+ @Test
+ fun chip_inCall_positiveStartTime_isShownAsTimer() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 345, intent = null))
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
}
@Test
@@ -91,11 +112,12 @@
// started 2000ms ago (1000 - 3000). The OngoingActivityChipModel start time needs to be
// relative to elapsedRealtime, so it should be 2000ms before the elapsed realtime set
// on the clock.
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(398_000)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs)
+ .isEqualTo(398_000)
}
@Test
- fun chip_inCall_iconIsPhone() =
+ fun chip_positiveStartTime_iconIsPhone() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -103,6 +125,43 @@
assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res)
.isEqualTo(com.android.internal.R.drawable.ic_phone)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon!!.contentDescription)
+ .isNotNull()
+ }
+
+ @Test
+ fun chip_zeroStartTime_iconIsPhone() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null))
+
+ assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res)
+ .isEqualTo(com.android.internal.R.drawable.ic_phone)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon!!.contentDescription)
+ .isNotNull()
+ }
+
+ @Test
+ fun chip_positiveStartTime_colorsAreThemed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors)
+ .isEqualTo(ColorsModel.Themed)
+ }
+
+ @Test
+ fun chip_zeroStartTime_colorsAreThemed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = null))
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors)
+ .isEqualTo(ColorsModel.Themed)
}
@Test
@@ -115,7 +174,8 @@
// Start a call
repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(398_000)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs)
+ .isEqualTo(398_000)
// End the call
repo.setOngoingCallState(OngoingCallModel.NoCall)
@@ -128,32 +188,46 @@
// Start a new call, which started 1000ms ago
repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 102_000, intent = null))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(499_000)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs)
+ .isEqualTo(499_000)
}
@Test
- fun chip_inCall_nullIntent_clickListenerDoesNothing() =
+ fun chip_inCall_nullIntent_nullClickListener() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
- val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
-
- clickListener.onClick(chipView)
- // Just verify nothing crashes
+ assertThat((latest as OngoingActivityChipModel.Shown).onClickListener).isNull()
}
@Test
- fun chip_inCall_validIntent_clickListenerLaunchesIntent() =
+ fun chip_inCall_positiveStartTime_validIntent_clickListenerLaunchesIntent() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
val intent = mock<PendingIntent>()
repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = intent))
val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
+
+ verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null)
+ }
+
+ @Test
+ fun chip_inCall_zeroStartTime_validIntent_clickListenerLaunchesIntent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ val intent = mock<PendingIntent>()
+ repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 0, intent = intent))
+ val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
+ assertThat(clickListener).isNotNull()
+
+ clickListener!!.onClick(chipView)
verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index 93d781f..74b6ae2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.CAST_TO_OTHER_DEVICES_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -90,7 +91,7 @@
}
@Test
- fun chip_singleTaskState_otherDevicesPackage_isShown() =
+ fun chip_singleTaskState_otherDevicesPackage_isShownAsTimer() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -100,22 +101,35 @@
createTask(taskId = 1),
)
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+ assertThat(icon.contentDescription).isNotNull()
}
@Test
- fun chip_entireScreenState_otherDevicesPackage_isShown() =
+ fun chip_entireScreenState_otherDevicesPackage_isShownAsTimer() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+ assertThat(icon.contentDescription).isNotNull()
+ }
+
+ @Test
+ fun chip_colorsAreRed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors).isEqualTo(ColorsModel.Red)
}
@Test
@@ -150,7 +164,7 @@
MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
@@ -163,7 +177,7 @@
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(5678)
}
@Test
@@ -174,8 +188,9 @@
MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(
eq(mockCastDialog),
@@ -197,8 +212,9 @@
)
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(
eq(mockCastDialog),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index 45044f7..0a06cc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -87,25 +88,82 @@
}
@Test
- fun chip_startingState_isHidden() =
+ fun chip_startingState_isShownAsCountdownWithoutIconOrClickListener() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(400)
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Countdown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon).isNull()
+ assertThat((latest as OngoingActivityChipModel.Shown).onClickListener).isNull()
+ }
+
+ // The millis we typically get from [ScreenRecordRepository] are around 2995, 1995, and 995.
+ @Test
+ fun chip_startingState_millis2995_is3() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(2995)
+
+ assertThat((latest as OngoingActivityChipModel.Shown.Countdown).secondsUntilStarted)
+ .isEqualTo(3)
}
@Test
- fun chip_recordingState_isShownWithIcon() =
+ fun chip_startingState_millis1995_is2() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(1995)
+
+ assertThat((latest as OngoingActivityChipModel.Shown.Countdown).secondsUntilStarted)
+ .isEqualTo(2)
+ }
+
+ @Test
+ fun chip_startingState_millis995_is1() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(995)
+
+ assertThat((latest as OngoingActivityChipModel.Shown.Countdown).secondsUntilStarted)
+ .isEqualTo(1)
+ }
+
+ @Test
+ fun chip_recordingState_isShownAsTimerWithIcon() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
+ assertThat(icon.contentDescription).isNotNull()
+ }
+
+ @Test
+ fun chip_startingState_colorsAreRed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(2000L)
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors).isEqualTo(ColorsModel.Red)
+ }
+
+ @Test
+ fun chip_recordingState_colorsAreRed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors).isEqualTo(ColorsModel.Red)
}
@Test
@@ -117,7 +175,7 @@
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
@@ -126,7 +184,7 @@
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(5678)
}
@Test
@@ -137,8 +195,9 @@
mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
// EndScreenRecordingDialogDelegate will test that the dialog has the right message
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(
@@ -158,8 +217,9 @@
MediaProjectionState.Projecting.EntireScreen("host.package")
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
// EndScreenRecordingDialogDelegate will test that the dialog has the right message
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(
@@ -182,8 +242,9 @@
)
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
// EndScreenRecordingDialogDelegate will test that the dialog has the right message
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
index 4c2546e..63c29ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
@@ -59,7 +59,7 @@
underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
- verify(sysuiDialog).setIcon(R.drawable.ic_screenshot_share)
+ verify(sysuiDialog).setIcon(R.drawable.ic_present_to_all)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index 9aef526..3028d00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -115,7 +116,7 @@
}
@Test
- fun chip_singleTaskState_normalPackage_isShown() =
+ fun chip_singleTaskState_normalPackage_isShownAsTimer() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
@@ -125,22 +126,35 @@
createTask(taskId = 1),
)
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
- assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
+ assertThat(icon.contentDescription).isNotNull()
}
@Test
- fun chip_entireScreenState_normalPackage_isShown() =
+ fun chip_entireScreenState_normalPackage_isShownAsTimer() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
- assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
+ assertThat(icon.contentDescription).isNotNull()
+ }
+
+ @Test
+ fun chip_colorsAreRed() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+ assertThat((latest as OngoingActivityChipModel.Shown).colors).isEqualTo(ColorsModel.Red)
}
@Test
@@ -153,7 +167,7 @@
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
@@ -166,7 +180,7 @@
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
- assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(5678)
}
@Test
@@ -177,8 +191,9 @@
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(
eq(mockShareDialog),
@@ -199,8 +214,9 @@
)
val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+ assertThat(clickListener).isNotNull()
- clickListener.onClick(chipView)
+ clickListener!!.onClick(chipView)
verify(kosmos.mockDialogTransitionAnimator)
.showFromView(
eq(mockShareDialog),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
index 7d2b463..5fbdfbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.chips.ui.view
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
@@ -29,7 +29,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ChipBackgroundContainerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
index b8d4e47..6f771175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.chips.ui.view
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
@@ -36,7 +36,7 @@
private const val XL_TEXT = "00:0000"
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ChipChronometerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index a7b1411f..8bc83cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
@@ -34,15 +35,22 @@
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
+ private val systemClock = kosmos.fakeSystemClock
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
@@ -188,6 +196,39 @@
assertIsCallChip(latest)
}
+ /** Regression test for b/347726238. */
+ @Test
+ fun chip_timerDoesNotResetAfterSubscribersRestart() =
+ testScope.runTest {
+ var latest: OngoingActivityChipModel? = null
+
+ val job1 = underTest.chip.onEach { latest = it }.launchIn(this)
+
+ // Start a chip with a timer
+ systemClock.setElapsedRealtime(1234)
+ screenRecordState.value = ScreenRecordModel.Recording
+
+ runCurrent()
+
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
+
+ // Stop subscribing to the chip flow
+ job1.cancel()
+
+ // Let time pass
+ systemClock.setElapsedRealtime(5678)
+
+ // WHEN we re-subscribe to the chip flow
+ val job2 = underTest.chip.onEach { latest = it }.launchIn(this)
+
+ runCurrent()
+
+ // THEN the old start time is still used
+ assertThat((latest as OngoingActivityChipModel.Shown.Timer).startTimeMs).isEqualTo(1234)
+
+ job2.cancel()
+ }
+
companion object {
fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
@@ -198,7 +239,7 @@
fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
- assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_present_to_all)
}
fun assertIsCallChip(latest: OngoingActivityChipModel?) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt
index cfbe8e3..673cf59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.commandline
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -24,8 +25,10 @@
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class CommandParserTest : SysuiTestCase() {
private val parser = CommandParser()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
index e391d6b..83811cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.statusbar.commandline
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -7,8 +8,10 @@
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ParametersTest : SysuiTestCase() {
@Test
fun singleArgOptional_returnsNullBeforeParse() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
index 86548d0..1a7c8a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.commandline
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -24,10 +25,12 @@
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ParseableCommandTest : SysuiTestCase() {
@Mock private lateinit var pw: PrintWriter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
index 759f0bc..8cf7473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
@@ -1,13 +1,16 @@
package com.android.systemui.statusbar.commandline
import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ValueParserTest : SysuiTestCase() {
@Test
fun parseString() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
index 0fdda62..ca90f74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProviderTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.connectivity.ui
import android.telephony.SubscriptionInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.demomode.DemoModeController
@@ -28,12 +29,14 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MobileContextProviderTest : SysuiTestCase() {
@Mock private lateinit var networkController: NetworkController
@Mock private lateinit var dumpManager: DumpManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt
index b1c994c..7c98037 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.data.repository
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
@@ -29,9 +30,11 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.verify
@SmallTest
+@RunWith(AndroidJUnit4::class)
class KeyguardStatusBarRepositoryImplTest : SysuiTestCase() {
private val testScope = TestScope()
private val configurationController = mock<ConfigurationController>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 4b250b2..7f981ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -23,6 +23,7 @@
import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
import android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS
import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
@@ -48,9 +49,11 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.verify
@SmallTest
+@RunWith(AndroidJUnit4::class)
class StatusBarModeRepositoryImplTest : SysuiTestCase() {
private val testScope = TestScope()
private val commandQueue = mock<CommandQueue>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
index f1c7956..fffcbb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.statusbar.disableflags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DisableFlagsLoggerTest : SysuiTestCase() {
private val disable1Flags = listOf(
DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt
index 215afb2..cf78c71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt
@@ -24,17 +24,20 @@
import android.app.StatusBarManager.DISABLE_NAVIGATION
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_NOTIFICATION_TICKER
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DisableStateTrackerTest : SysuiTestCase() {
private lateinit var underTest: DisableStateTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
index 31e1fef..d2dfc92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
@@ -21,6 +21,7 @@
import android.app.StatusBarManager.DISABLE_NONE
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
import android.content.res.Configuration
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -42,10 +43,12 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.verify
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class DisableFlagsRepositoryTest : SysuiTestCase() {
private lateinit var underTest: DisableFlagsRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index b944d72..7a8533e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -35,6 +35,7 @@
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
@@ -69,6 +70,7 @@
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
@@ -86,6 +88,7 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
class LockscreenSmartspaceControllerTest : SysuiTestCase() {
companion object {
const val SMARTSPACE_TIME_TOO_EARLY = 1000L
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index b161f84..0505727 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -23,6 +23,7 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
@@ -54,11 +55,13 @@
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@SmallTest
+@RunWith(AndroidJUnit4::class)
class SensitiveContentCoordinatorTest : SysuiTestCase() {
val dynamicPrivacyController: DynamicPrivacyController = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
index 8272f5a..ef2a2aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
@@ -19,6 +19,7 @@
import android.app.smartspace.SmartspaceTarget
import android.content.ComponentName
import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
@@ -43,6 +44,7 @@
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
@@ -51,6 +53,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class SmartspaceDedupingCoordinatorTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 11996fe..99bd4fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.render
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.logcatLogBuffer
@@ -37,10 +38,12 @@
import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.`when` as whenever
@SmallTest
+@RunWith(AndroidJUnit4::class)
class NodeSpecBuilderTest : SysuiTestCase() {
private val mediaContainerController: MediaContainerController = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
index 70d309b..ca75ca6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.render
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.GroupEntry
@@ -32,6 +33,7 @@
import com.android.systemui.util.mockito.withArgCaptor
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
@@ -42,6 +44,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class RenderStageManagerTest : SysuiTestCase() {
@Mock private lateinit var shadeListBuilder: ShadeListBuilder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
index b775079..79ff4be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
@@ -15,6 +15,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
import android.app.StatusBarManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
@@ -26,8 +27,10 @@
import dagger.BindsInstance
import dagger.Component
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class NotificationAlertsInteractorTest : SysuiTestCase() {
@Component(modules = [SysUITestModule::class])
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt
index a0faab5..982b7b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -23,8 +24,10 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class NotificationLaunchAnimationInteractorTest : SysuiTestCase() {
private val repository = NotificationLaunchAnimationRepository()
private val underTest = NotificationLaunchAnimationInteractor(repository)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
index 3593f5b..133a114 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -13,6 +13,7 @@
*/
package com.android.systemui.statusbar.notification.domain.interactor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
@@ -25,8 +26,10 @@
import dagger.BindsInstance
import dagger.Component
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class NotificationsKeyguardInteractorTest : SysuiTestCase() {
@SysUISingleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 334776c..277b887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
import android.service.notification.StatusBarNotification
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -31,8 +32,10 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class RenderNotificationsListInteractorTest : SysuiTestCase() {
private val backgroundDispatcher = StandardTestDispatcher()
private val testScope = TestScope(backgroundDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 7304bd6..164a06e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import org.junit.Assert;
@@ -328,7 +329,7 @@
@Test
@EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME)
- public void setHideSensitive_changeContent_shouldNotDisturbAnimation() throws Exception {
+ public void setHideSensitive_changeContent_shouldResetAlpha() throws Exception {
// Given: A sensitive row that has public version but is not hiding sensitive,
// and is during an animation that sets its alpha value to be 0.5f
@@ -351,12 +352,12 @@
// Then: The alpha value of private layout should be reset to 1, private layout be
// INVISIBLE;
- // The alpha value of public layout should be 0.5 to preserve the animation state, public
- // layout should be VISIBLE
+ // The alpha value of public layout should be reset to 1 to avoid remaining transparent,
+ // public layout should be VISIBLE
assertEquals(View.INVISIBLE, row.getPrivateLayout().getVisibility());
assertEquals(1f, row.getPrivateLayout().getAlpha(), 0);
assertEquals(View.VISIBLE, row.getPublicLayout().getVisibility());
- assertEquals(0.5f, row.getPublicLayout().getAlpha(), 0);
+ assertEquals(1f, row.getPublicLayout().getAlpha(), 0);
}
@Test
@@ -793,6 +794,200 @@
}
@Test
+ public void isExpanded_onKeyguard_allowOnKeyguardExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(true);
+ row.setUserExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded(/*allowOnKeyguard =*/ true)).isTrue();
+ }
+ @Test
+ public void isExpanded_onKeyguard_notAllowOnKeyguardNotExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(true);
+ row.setUserExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded(/*allowOnKeyguard =*/ false)).isFalse();
+ }
+
+ @Test
+ public void isExpanded_systemExpanded_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ public void isExpanded_systemChildExpanded_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemChildExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ public void isExpanded_userExpanded_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setUserExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ public void isExpanded_userExpandedFalse_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setUserExpanded(false);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_systemExpandedTrueForHeadsUp_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUp(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_systemExpandedTrueForHeadsUpDisappearRunning_notExpanded()
+ throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedTrueForHeadsUp_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedTrueForHeadsUpDisappearRunning_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedFalseForHeadsUp_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(false);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_userExpandedFalseForHeadsUpDisappearRunning_notExpanded()
+ throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUpAnimatingAway(true);
+ row.setUserExpanded(false);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_HUNexpandedWhenPinningTrue_expanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(true);
+ row.setHeadsUp(true);
+ row.setPinned(true);
+
+ // WHEN
+ row.expandNotification();
+
+ // THEN
+ assertThat(row.isExpanded()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void isExpanded_HUNexpandedWhenPinningFalse_notExpanded() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setOnKeyguard(false);
+ row.setSystemExpanded(false);
+ row.setHeadsUp(true);
+ row.setPinned(true);
+
+ // THEN
+ assertThat(row.isExpanded()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void hasUserChangedExpansion_expandPinned_returnTrue() throws Exception {
+ // GIVEN
+ final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+ row.setPinned(true);
+
+ // WHEN
+ row.expandNotification();
+
+ // THEN
+ assertThat(row.hasUserChangedExpansion()).isTrue();
+ }
+
+ @Test
public void onDisappearAnimationFinished_shouldSetFalse_headsUpAnimatingAway()
throws Exception {
final ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index e6cba1c..54a26f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -23,6 +23,7 @@
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import android.util.TypedValue
+import android.util.TypedValue.COMPLEX_UNIT_SP
import android.view.View
import android.view.ViewGroup
import android.widget.RemoteViews
@@ -34,27 +35,39 @@
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel
import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
+import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
import com.android.systemui.statusbar.policy.InflatedSmartReplyState
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder
import com.android.systemui.statusbar.policy.SmartReplyStateInflater
-import com.android.systemui.util.concurrency.mockExecutorHandler
+import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.DisposableHandle
import org.junit.Assert
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
+import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -65,20 +78,24 @@
@RunWithLooper
@EnableFlags(NotificationRowContentBinderRefactor.FLAG_NAME)
class NotificationRowContentBinderImplTest : SysuiTestCase() {
- private lateinit var mNotificationInflater: NotificationRowContentBinderImpl
- private lateinit var mBuilder: Notification.Builder
- private lateinit var mRow: ExpandableNotificationRow
- private lateinit var mHelper: NotificationTestHelper
+ private lateinit var notificationInflater: NotificationRowContentBinderImpl
+ private lateinit var builder: Notification.Builder
+ private lateinit var row: ExpandableNotificationRow
+ private lateinit var testHelper: NotificationTestHelper
- private var mCache: NotifRemoteViewCache = mock()
- private var mConversationNotificationProcessor: ConversationNotificationProcessor = mock()
- private var mInflatedSmartReplyState: InflatedSmartReplyState = mock()
- private var mInflatedSmartReplies: InflatedSmartReplyViewHolder = mock()
- private var mNotifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider = mock()
- private var mHeadsUpStyleProvider: HeadsUpStyleProvider = mock()
- private var mNotifLayoutInflaterFactory: NotifLayoutInflaterFactory = mock()
- private val mSmartReplyStateInflater: SmartReplyStateInflater =
+ private val cache: NotifRemoteViewCache = mock()
+ private val layoutInflaterFactoryProvider =
+ object : NotifLayoutInflaterFactory.Provider {
+ override fun provide(
+ row: ExpandableNotificationRow,
+ layoutType: Int
+ ): NotifLayoutInflaterFactory = mock()
+ }
+ private val smartReplyStateInflater: SmartReplyStateInflater =
object : SmartReplyStateInflater {
+ private val inflatedSmartReplyState: InflatedSmartReplyState = mock()
+ private val inflatedSmartReplies: InflatedSmartReplyViewHolder = mock()
+
override fun inflateSmartReplyViewHolder(
sysuiContext: Context,
notifPackageContext: Context,
@@ -86,37 +103,61 @@
existingSmartReplyState: InflatedSmartReplyState?,
newSmartReplyState: InflatedSmartReplyState
): InflatedSmartReplyViewHolder {
- return mInflatedSmartReplies
+ return inflatedSmartReplies
}
override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState {
- return mInflatedSmartReplyState
+ return inflatedSmartReplyState
}
}
+ private var fakeRonContentModel: RichOngoingContentModel? = null
+ private val fakeRonExtractor =
+ object : RichOngoingNotificationContentExtractor {
+ override fun extractContentModel(
+ entry: NotificationEntry,
+ builder: Notification.Builder,
+ systemUIContext: Context,
+ packageContext: Context
+ ): RichOngoingContentModel? = fakeRonContentModel
+ }
+
+ private var fakeRonViewHolder: InflatedContentViewHolder? = null
+ private val fakeRonViewInflater =
+ spy(
+ object : RichOngoingNotificationViewInflater {
+ override fun inflateView(
+ contentModel: RichOngoingContentModel,
+ existingView: View?,
+ entry: NotificationEntry,
+ systemUiContext: Context,
+ parentView: ViewGroup
+ ): InflatedContentViewHolder? = fakeRonViewHolder
+ }
+ )
+
@Before
fun setUp() {
allowTestableLooperAsMainThread()
- mBuilder =
+ builder =
Notification.Builder(mContext, "no-id")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text")
.setStyle(Notification.BigTextStyle().bigText("big text"))
- mHelper = NotificationTestHelper(mContext, mDependency)
- val row = mHelper.createRow(mBuilder.build())
- mRow = spy(row)
- whenever(mNotifLayoutInflaterFactoryProvider.provide(any(), any()))
- .thenReturn(mNotifLayoutInflaterFactory)
- mNotificationInflater =
+ testHelper = NotificationTestHelper(mContext, mDependency)
+ row = spy(testHelper.createRow(builder.build()))
+ notificationInflater =
NotificationRowContentBinderImpl(
- mCache,
+ cache,
mock(),
- mConversationNotificationProcessor,
+ mock<ConversationNotificationProcessor>(),
+ fakeRonExtractor,
+ fakeRonViewInflater,
mock(),
- mSmartReplyStateInflater,
- mNotifLayoutInflaterFactoryProvider,
- mHeadsUpStyleProvider,
+ smartReplyStateInflater,
+ layoutInflaterFactoryProvider,
+ mock<HeadsUpStyleProvider>(),
mock()
)
}
@@ -125,16 +166,16 @@
fun testIncreasedHeadsUpBeingUsed() {
val params = BindParams()
params.usesIncreasedHeadsUpHeight = true
- val builder = spy(mBuilder)
- mNotificationInflater.inflateNotificationViews(
- mRow.entry,
- mRow,
+ val builder = spy(builder)
+ notificationInflater.inflateNotificationViews(
+ row.entry,
+ row,
params,
true /* inflateSynchronously */,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+ FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
- mSmartReplyStateInflater
+ smartReplyStateInflater
)
verify(builder).createHeadsUpContentView(true)
}
@@ -143,80 +184,68 @@
fun testIncreasedHeightBeingUsed() {
val params = BindParams()
params.usesIncreasedHeight = true
- val builder = spy(mBuilder)
- mNotificationInflater.inflateNotificationViews(
- mRow.entry,
- mRow,
+ val builder = spy(builder)
+ notificationInflater.inflateNotificationViews(
+ row.entry,
+ row,
params,
true /* inflateSynchronously */,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+ FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
- mSmartReplyStateInflater
+ smartReplyStateInflater
)
verify(builder).createContentView(true)
}
@Test
fun testInflationCallsUpdated() {
- inflateAndWait(
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
- mRow
- )
- verify(mRow).onNotificationUpdated()
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+ verify(row).onNotificationUpdated()
}
@Test
fun testInflationOnlyInflatesSetFlags() {
- inflateAndWait(
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP,
- mRow
- )
- Assert.assertNotNull(mRow.privateLayout.headsUpChild)
- verify(mRow).onNotificationUpdated()
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
+ Assert.assertNotNull(row.privateLayout.headsUpChild)
+ verify(row).onNotificationUpdated()
}
@Test
fun testInflationThrowsErrorDoesntCallUpdated() {
- mRow.privateLayout.removeAllViews()
- mRow.entry.sbn.notification.contentView =
+ row.privateLayout.removeAllViews()
+ row.entry.sbn.notification.contentView =
RemoteViews(mContext.packageName, R.layout.status_bar)
inflateAndWait(
true /* expectingException */,
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
- mRow
+ notificationInflater,
+ FLAG_CONTENT_VIEW_ALL,
+ row
)
- Assert.assertTrue(mRow.privateLayout.childCount == 0)
- verify(mRow, times(0)).onNotificationUpdated()
+ Assert.assertTrue(row.privateLayout.childCount == 0)
+ verify(row, times(0)).onNotificationUpdated()
}
@Test
fun testAsyncTaskRemoved() {
- mRow.entry.abortTask()
- inflateAndWait(
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
- mRow
- )
- verify(mRow).onNotificationUpdated()
+ row.entry.abortTask()
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+ verify(row).onNotificationUpdated()
}
@Test
fun testRemovedNotInflated() {
- mRow.setRemoved()
- mNotificationInflater.setInflateSynchronously(true)
- mNotificationInflater.bindContent(
- mRow.entry,
- mRow,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+ row.setRemoved()
+ notificationInflater.setInflateSynchronously(true)
+ notificationInflater.bindContent(
+ row.entry,
+ row,
+ FLAG_CONTENT_VIEW_ALL,
BindParams(),
false /* forceInflate */,
null /* callback */
)
- Assert.assertNull(mRow.entry.runningTask)
+ Assert.assertNull(row.entry.runningTask)
}
@Test
@@ -235,11 +264,11 @@
inflateSynchronously = false,
isMinimized = false,
result = result,
- reInflateFlags = NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED,
+ reInflateFlags = FLAG_CONTENT_VIEW_EXPANDED,
inflationId = 0,
remoteViewCache = mock(),
- entry = mRow.entry,
- row = mRow,
+ entry = row.entry,
+ row = row,
isNewView = true, /* isNewView */
remoteViewClickHandler = { _, _, _ -> true },
callback =
@@ -253,7 +282,7 @@
countDownLatch.countDown()
}
},
- parentLayout = mRow.privateLayout,
+ parentLayout = row.privateLayout,
existingView = null,
existingWrapper = null,
runningInflations = HashMap(),
@@ -275,13 +304,13 @@
@Test
fun doesntReapplyDisallowedRemoteView() {
- mBuilder.setStyle(Notification.MediaStyle())
- val mediaView = mBuilder.createContentView()
- mBuilder.setStyle(Notification.DecoratedCustomViewStyle())
- mBuilder.setCustomContentView(
+ builder.setStyle(Notification.MediaStyle())
+ val mediaView = builder.createContentView()
+ builder.setStyle(Notification.DecoratedCustomViewStyle())
+ builder.setCustomContentView(
RemoteViews(context.packageName, com.android.systemui.tests.R.layout.custom_view_dark)
)
- val decoratedMediaView = mBuilder.createContentView()
+ val decoratedMediaView = builder.createContentView()
Assert.assertFalse(
"The decorated media style doesn't allow a view to be reapplied!",
NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView)
@@ -292,112 +321,167 @@
@Ignore("b/345418902")
fun testUsesSameViewWhenCachedPossibleToReuse() {
// GIVEN a cached view.
- val contractedRemoteView = mBuilder.createContentView()
- whenever(
- mCache.hasCachedView(
- mRow.entry,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
- )
- )
- .thenReturn(true)
- whenever(
- mCache.getCachedView(
- mRow.entry,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
- )
- )
+ val contractedRemoteView = builder.createContentView()
+ whenever(cache.hasCachedView(row.entry, FLAG_CONTENT_VIEW_CONTRACTED)).thenReturn(true)
+ whenever(cache.getCachedView(row.entry, FLAG_CONTENT_VIEW_CONTRACTED))
.thenReturn(contractedRemoteView)
// GIVEN existing bound view with same layout id.
val view = contractedRemoteView.apply(mContext, null /* parent */)
- mRow.privateLayout.setContractedChild(view)
+ row.privateLayout.setContractedChild(view)
// WHEN inflater inflates
- inflateAndWait(
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED,
- mRow
- )
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
// THEN the view should be re-used
Assert.assertEquals(
"Binder inflated a new view even though the old one was cached and usable.",
view,
- mRow.privateLayout.contractedChild
+ row.privateLayout.contractedChild
)
}
@Test
fun testInflatesNewViewWhenCachedNotPossibleToReuse() {
// GIVEN a cached remote view.
- val contractedRemoteView = mBuilder.createHeadsUpContentView()
- whenever(
- mCache.hasCachedView(
- mRow.entry,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
- )
- )
- .thenReturn(true)
- whenever(
- mCache.getCachedView(
- mRow.entry,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
- )
- )
+ val contractedRemoteView = builder.createHeadsUpContentView()
+ whenever(cache.hasCachedView(row.entry, FLAG_CONTENT_VIEW_CONTRACTED)).thenReturn(true)
+ whenever(cache.getCachedView(row.entry, FLAG_CONTENT_VIEW_CONTRACTED))
.thenReturn(contractedRemoteView)
// GIVEN existing bound view with different layout id.
val view: View = TextView(mContext)
- mRow.privateLayout.setContractedChild(view)
+ row.privateLayout.setContractedChild(view)
// WHEN inflater inflates
- inflateAndWait(
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED,
- mRow
- )
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
// THEN the view should be a new view
Assert.assertNotEquals(
"Binder (somehow) used the same view when inflating.",
view,
- mRow.privateLayout.contractedChild
+ row.privateLayout.contractedChild
)
}
@Test
fun testInflationCachesCreatedRemoteView() {
// WHEN inflater inflates
- inflateAndWait(
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED,
- mRow
- )
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
// THEN inflater informs cache of the new remote view
- verify(mCache)
- .putCachedView(
- eq(mRow.entry),
- eq(NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED),
- any()
- )
+ verify(cache).putCachedView(eq(row.entry), eq(FLAG_CONTENT_VIEW_CONTRACTED), any())
}
@Test
fun testUnbindRemovesCachedRemoteView() {
// WHEN inflated unbinds content
- mNotificationInflater.unbindContent(
- mRow.entry,
- mRow,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
- )
+ notificationInflater.unbindContent(row.entry, row, FLAG_CONTENT_VIEW_HEADS_UP)
// THEN inflated informs cache to remove remote view
- verify(mCache)
- .removeCachedView(
- eq(mRow.entry),
- eq(NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP)
- )
+ verify(cache).removeCachedView(eq(row.entry), eq(FLAG_CONTENT_VIEW_HEADS_UP))
+ }
+
+ @Test
+ fun testRonModelRequiredForRonView() {
+ fakeRonContentModel = null
+ val ronView = View(context)
+ fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mock())
+ // WHEN inflater inflates
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+ verify(fakeRonViewInflater, never()).inflateView(any(), any(), any(), any(), any())
+ }
+
+ @Test
+ fun testRonModelTriggersInflationOfRonView() {
+ val mockRonModel = mock<TimerContentModel>()
+ val ronView = View(context)
+ val mockBinder = mock<DeferredContentViewBinder>()
+
+ val entry = row.entry
+ val privateLayout = row.privateLayout
+
+ fakeRonContentModel = mockRonModel
+ fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+ // WHEN inflater inflates
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+ // VERIFY that the inflater is invoked
+ verify(fakeRonViewInflater)
+ .inflateView(eq(mockRonModel), any(), eq(entry), any(), eq(privateLayout))
+ assertThat(row.privateLayout.contractedChild).isSameInstanceAs(ronView)
+ verify(mockBinder).setupContentViewBinder()
+ }
+
+ @Test
+ fun ronViewAppliesElementsInOrder() {
+ val oldHandle = mock<DisposableHandle>()
+ val mockRonModel = mock<TimerContentModel>()
+ val ronView = View(context)
+ val mockBinder = mock<DeferredContentViewBinder>()
+
+ row.privateLayout.mContractedBinderHandle = oldHandle
+ val entry = spy(row.entry)
+ row.entry = entry
+ val privateLayout = spy(row.privateLayout)
+ row.privateLayout = privateLayout
+
+ fakeRonContentModel = mockRonModel
+ fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+ // WHEN inflater inflates
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
+ // Validate that these 4 steps happen in this precise order
+ inOrder(oldHandle, entry, privateLayout, mockBinder) {
+ verify(oldHandle).dispose()
+ verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+ verify(privateLayout).setContractedChild(eq(ronView))
+ verify(mockBinder).setupContentViewBinder()
+ }
+ }
+
+ @Test
+ fun testRonNotReinflating() {
+ val handle0 = mock<DisposableHandle>()
+ val handle1 = mock<DisposableHandle>()
+ val ronView = View(context)
+ val mockRonModel1 = mock<TimerContentModel>()
+ val mockRonModel2 = mock<TimerContentModel>()
+ val mockBinder1 = mock<DeferredContentViewBinder>()
+ doReturn(handle1).whenever(mockBinder1).setupContentViewBinder()
+
+ row.privateLayout.mContractedBinderHandle = handle0
+ val entry = spy(row.entry)
+ row.entry = entry
+ val privateLayout = spy(row.privateLayout)
+ row.privateLayout = privateLayout
+
+ // WHEN inflater inflates both a model and a view
+ fakeRonContentModel = mockRonModel1
+ fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder1)
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
+ // Validate that these 4 steps happen in this precise order
+ inOrder(handle0, entry, privateLayout, mockBinder1, handle1) {
+ verify(handle0).dispose()
+ verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel1 })
+ verify(privateLayout).setContractedChild(eq(ronView))
+ verify(mockBinder1).setupContentViewBinder()
+ verify(handle1, never()).dispose()
+ }
+
+ clearInvocations(handle0, entry, privateLayout, mockBinder1, handle1)
+
+ // THEN when the inflater inflates just a model
+ fakeRonContentModel = mockRonModel2
+ fakeRonViewHolder = null
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
+ // Validate that for reinflation, the only thing we do us update the model
+ verify(handle1, never()).dispose()
+ verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel2 })
+ verify(privateLayout, never()).setContractedChild(any())
+ verify(mockBinder1, never()).setupContentViewBinder()
+ verify(handle1, never()).dispose()
}
@Test
@@ -453,46 +537,36 @@
whenever(view.measuredHeight)
.thenReturn(
TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_SP,
+ COMPLEX_UNIT_SP,
measuredHeightDp,
mContext.resources.displayMetrics
)
.toInt()
)
- mRow.entry.targetSdk = targetSdk
- mRow.entry.sbn.notification.contentView = contentView
- return NotificationRowContentBinderImpl.isValidView(view, mRow.entry, mContext.resources)
+ row.entry.targetSdk = targetSdk
+ row.entry.sbn.notification.contentView = contentView
+ return NotificationRowContentBinderImpl.isValidView(view, row.entry, mContext.resources)
}
@Test
fun testInvalidNotificationDoesNotInvokeCallback() {
- mRow.privateLayout.removeAllViews()
- mRow.entry.sbn.notification.contentView =
+ row.privateLayout.removeAllViews()
+ row.entry.sbn.notification.contentView =
RemoteViews(
mContext.packageName,
com.android.systemui.tests.R.layout.invalid_notification_height
)
- inflateAndWait(
- true,
- mNotificationInflater,
- NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
- mRow
- )
- Assert.assertEquals(0, mRow.privateLayout.childCount.toLong())
- verify(mRow, times(0)).onNotificationUpdated()
+ inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+ Assert.assertEquals(0, row.privateLayout.childCount.toLong())
+ verify(row, times(0)).onNotificationUpdated()
}
private class ExceptionHolder {
- var mException: Exception? = null
-
- fun setException(exception: Exception?) {
- mException = exception
- }
+ var exception: Exception? = null
}
private class AsyncFailRemoteView(packageName: String?, layoutId: Int) :
RemoteViews(packageName, layoutId) {
- var mHandler = mockExecutorHandler { p0 -> p0.run() }
override fun apply(context: Context, parent: ViewGroup): View {
return super.apply(context, parent)
@@ -505,7 +579,7 @@
listener: OnViewAppliedListener,
handler: InteractionHandler?
): CancellationSignal {
- mHandler.post { listener.onError(RuntimeException("Failed to inflate async")) }
+ executor.execute { listener.onError(RuntimeException("Failed to inflate async")) }
return CancellationSignal()
}
@@ -541,18 +615,17 @@
object : InflationCallback {
override fun handleInflationException(entry: NotificationEntry, e: Exception) {
if (!expectingException) {
- exceptionHolder.setException(e)
+ exceptionHolder.exception = e
}
countDownLatch.countDown()
}
override fun onAsyncInflationFinished(entry: NotificationEntry) {
if (expectingException) {
- exceptionHolder.setException(
+ exceptionHolder.exception =
RuntimeException(
"Inflation finished even though there should be an error"
)
- )
}
countDownLatch.countDown()
}
@@ -566,7 +639,7 @@
callback /* callback */
)
Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
- exceptionHolder.mException?.let { throw it }
+ exceptionHolder.exception?.let { throw it }
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 21d586b..c74a04f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -199,6 +199,8 @@
mock(NotifRemoteViewCache.class),
mock(NotificationRemoteInputManager.class),
mock(ConversationNotificationProcessor.class),
+ mock(RichOngoingNotificationContentExtractor.class),
+ mock(RichOngoingNotificationViewInflater.class),
mock(Executor.class),
new MockSmartReplyInflater(),
mock(NotifLayoutInflaterFactory.Provider.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt
index 1dfcb38..578950f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/DisplaySwitchNotificationsHiderTrackerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar.notification.stack
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.util.LatencyTracker.ACTION_NOTIFICATIONS_HIDDEN_FOR_MEASURE
@@ -31,12 +32,14 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class DisplaySwitchNotificationsHiderTrackerTest : SysuiTestCase() {
private val testScope = TestScope()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 48e8f88..c1f2cb77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -10,6 +10,8 @@
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.res.R
@@ -30,8 +32,8 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/** Tests for {@link NotificationShelf}. */
@SmallTest
@@ -332,6 +334,101 @@
}
@Test
+ fun updateState_lastViewAlmostBelowShelf_completelyInShelf() {
+ val viewStart = 0f
+ val shelfClipStart = 0.001f
+
+ val expandableView = mock(ExpandableView::class.java)
+ whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+ whenever(expandableView.translationY).thenReturn(viewStart)
+ whenever(expandableView.actualHeight).thenReturn(20)
+
+ whenever(expandableView.minHeight).thenReturn(20)
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.isInShelf).thenReturn(true)
+
+ whenever(ambientState.isOnKeyguard).thenReturn(true)
+ whenever(ambientState.isExpansionChanging).thenReturn(false)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+
+ val amountInShelf =
+ shelf.getAmountInShelf(
+ /* i= */ 0,
+ /* view= */ expandableView,
+ /* scrollingFast= */ false,
+ /* expandingAnimated= */ false,
+ /* isLastChild= */ true,
+ shelfClipStart
+ )
+ assertEquals(1f, amountInShelf)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun updateState_withViewInShelf_showShelf() {
+ // GIVEN a view is scrolled into the shelf
+ val stackTop = 200f
+ val stackHeight = 800f
+ whenever(ambientState.stackTop).thenReturn(stackTop)
+ whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ val shelfTop = stackTop + stackHeight - shelf.height
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ val viewInShelf = mock(ExpandableView::class.java)
+
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(viewInShelf)
+ whenever(viewInShelf.viewState).thenReturn(ExpandableViewState())
+ whenever(viewInShelf.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+ whenever(viewInShelf.translationY).thenReturn(shelfTop)
+ whenever(viewInShelf.actualHeight).thenReturn(10)
+ whenever(viewInShelf.isInShelf).thenReturn(true)
+ whenever(viewInShelf.minHeight).thenReturn(10)
+ whenever(viewInShelf.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(viewInShelf.isInShelf).thenReturn(true)
+
+ stackScrollAlgorithmState.visibleChildren.add(viewInShelf)
+ stackScrollAlgorithmState.firstViewInShelf = viewInShelf
+
+ // WHEN Shelf's ViewState is updated
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN the shelf is visible, and positioned correctly
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(false, shelfState.hidden)
+ assertEquals(shelf.height, shelfState.height)
+ assertEquals(shelfTop, shelfState.yTranslation)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun updateState_withNullLastVisibleBackgroundChild_hideShelf_withSceneContainer() {
+ // GIVEN
+ val stackTop = 200f
+ val stackHeight = 800f
+ whenever(ambientState.stackTop).thenReturn(stackTop)
+ whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ stackScrollAlgorithmState.firstViewInShelf = mock()
+
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(null)
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackTop + stackHeight + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -358,6 +455,35 @@
}
@Test
+ @EnableSceneContainer
+ fun updateState_withNullFirstViewInShelf_hideShelf_withSceneContainer() {
+ // GIVEN
+ val stackTop = 200f
+ val stackHeight = 800f
+ whenever(ambientState.stackTop).thenReturn(stackTop)
+ whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+
+ stackScrollAlgorithmState.firstViewInShelf = null
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackTop + stackHeight + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withNullFirstViewInShelf_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -384,6 +510,35 @@
}
@Test
+ @EnableSceneContainer
+ fun updateState_withCollapsedShade_hideShelf_withSceneContainer() {
+ // GIVEN
+ val stackTop = 200f
+ val stackHeight = 800f
+ whenever(ambientState.stackTop).thenReturn(stackTop)
+ whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ stackScrollAlgorithmState.firstViewInShelf = mock()
+
+ whenever(ambientState.isShadeExpanded).thenReturn(false)
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackTop + stackHeight + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withCollapsedShade_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -410,6 +565,49 @@
}
@Test
+ @EnableSceneContainer
+ fun updateState_withHiddenSectionBeforeShelf_hideShelf_withSceneContainer() {
+ // GIVEN
+ val stackTop = 200f
+ val stackHeight = 800f
+ whenever(ambientState.stackTop).thenReturn(stackTop)
+ whenever(ambientState.stackHeight).thenReturn(stackHeight)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+
+ val ssaVisibleChild = mock<ExpandableView>()
+ val ssaVisibleChildState = ExpandableViewState()
+ ssaVisibleChildState.hidden = true
+ whenever(ssaVisibleChild.viewState).thenReturn(ssaVisibleChildState)
+
+ val ssaVisibleChild1 = mock<ExpandableView>()
+ val ssaVisibleChildState1 = ExpandableViewState()
+ ssaVisibleChildState1.hidden = true
+ whenever(ssaVisibleChild1.viewState).thenReturn(ssaVisibleChildState1)
+
+ stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild)
+ stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild1)
+ whenever(ambientState.isExpansionChanging).thenReturn(true)
+ whenever(ambientState.expansionFraction).thenReturn(1f)
+ stackScrollAlgorithmState.firstViewInShelf = ssaVisibleChild1
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackTop + stackHeight + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -461,12 +659,9 @@
expectedAlpha: Float
) {
val sbnMock: StatusBarNotification = mock()
- val mockEntry = mock<NotificationEntry>().apply {
- whenever(this.sbn).thenReturn(sbnMock)
- }
+ val mockEntry = mock<NotificationEntry>().apply { whenever(this.sbn).thenReturn(sbnMock) }
val row = ExpandableNotificationRow(mContext, null, mockEntry)
- whenever(ambientState.lastVisibleBackgroundChild)
- .thenReturn(row)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(row)
whenever(ambientState.isExpansionChanging).thenReturn(true)
whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
whenever(hostLayoutController.speedBumpIndex).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 12f3ef3..770c424 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -214,6 +214,7 @@
}
@Test
+ @DisableSceneContainer // TODO(b/332574413) cover stack bounds integration with tests
public void testUpdateStackHeight_qsExpansionGreaterThanZero() {
final float expansionFraction = 0.2f;
final float overExpansion = 50f;
@@ -261,15 +262,62 @@
}
@Test
- public void updateStackEndHeightAndStackHeight_normallyUpdatesBoth() {
- final float expansionFraction = 0.5f;
+ @EnableSceneContainer
+ public void updateStackEndHeightAndStackHeight_shadeFullyExpanded_withSceneContainer() {
+ final float stackTop = 200f;
+ final float stackCutoff = 1000f;
+ final float stackEndHeight = stackCutoff - stackTop;
+ mAmbientState.setStackTop(stackTop);
+ mAmbientState.setStackCutoff(stackCutoff);
mAmbientState.setStatusBarState(StatusBarState.KEYGUARD);
-
- // Validate that by default we update everything
clearInvocations(mAmbientState);
+
+ // WHEN shade is fully expanded
+ mStackScroller.updateStackEndHeightAndStackHeight(/* fraction = */ 1.0f);
+
+ // THEN stackHeight and stackEndHeight are the same
+ verify(mAmbientState).setStackEndHeight(stackEndHeight);
+ verify(mAmbientState).setStackHeight(stackEndHeight);
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void updateStackEndHeightAndStackHeight_shadeExpanding_withSceneContainer() {
+ final float stackTop = 200f;
+ final float stackCutoff = 1000f;
+ final float stackEndHeight = stackCutoff - stackTop;
+ mAmbientState.setStackTop(stackTop);
+ mAmbientState.setStackCutoff(stackCutoff);
+ mAmbientState.setStatusBarState(StatusBarState.KEYGUARD);
+ clearInvocations(mAmbientState);
+
+ // WHEN shade is expanding
+ final float expansionFraction = 0.5f;
mStackScroller.updateStackEndHeightAndStackHeight(expansionFraction);
- verify(mAmbientState).setStackEndHeight(anyFloat());
- verify(mAmbientState).setStackHeight(anyFloat());
+
+ // THEN stackHeight is changed by the expansion frac
+ verify(mAmbientState).setStackEndHeight(stackEndHeight);
+ verify(mAmbientState).setStackHeight(stackEndHeight * 0.75f);
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void updateStackEndHeightAndStackHeight_shadeOverscrolledToTop_withSceneContainer() {
+ // GIVEN stack scrolled over the top, stack top is negative
+ final float stackTop = -2000f;
+ final float stackCutoff = 1000f;
+ final float stackEndHeight = stackCutoff - stackTop;
+ mAmbientState.setStackTop(stackTop);
+ mAmbientState.setStackCutoff(stackCutoff);
+ mAmbientState.setStatusBarState(StatusBarState.KEYGUARD);
+ clearInvocations(mAmbientState);
+
+ // WHEN stack is updated
+ mStackScroller.updateStackEndHeightAndStackHeight(/* fraction = */ 1.0f);
+
+ // THEN stackHeight is measured from the stack top
+ verify(mAmbientState).setStackEndHeight(stackEndHeight);
+ verify(mAmbientState).setStackHeight(stackEndHeight);
}
@Test
@@ -891,6 +939,7 @@
}
@Test
+ @DisableSceneContainer // NSSL has no more scroll logic when SceneContainer is on
public void testNormalShade_hasNoTopOverscroll() {
mTestableResources
.addOverride(R.bool.config_use_split_notification_shade, /* value= */ false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index a6fb718..b12c098 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -5,11 +5,15 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.res.R
@@ -37,6 +41,7 @@
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
@@ -44,6 +49,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
+@RunWith(AndroidJUnit4::class)
class StackScrollAlgorithmTest : SysuiTestCase() {
@JvmField @Rule var expect: Expect = Expect.create()
@@ -63,7 +69,7 @@
EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
- private val footerView = FooterView(context, /*attrs=*/ null)
+ private val footerView = FooterView(context, /* attrs= */ null)
@OptIn(ExperimentalCoroutinesApi::class)
private val ambientState =
AmbientState(
@@ -123,6 +129,7 @@
}
@Test
+ @DisableSceneContainer // TODO(b/332574413) cover hun bounds integration with tests
fun resetViewStates_defaultHunWhenShadeIsOpening_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
@@ -165,6 +172,7 @@
}
@Test
+ @DisableSceneContainer // TODO(b/332574413) cover hun bounds integration with tests
@EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun resetViewStates_defaultHun_showingQS_newHeadsUpAnim_hunTranslatedToMax() {
// Given: the shade is open and scrolled to the bottom to show the QuickSettings
@@ -181,6 +189,7 @@
}
@Test
+ @DisableSceneContainer // TODO(b/332574413) cover hun bounds integration with tests
@EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun resetViewStates_hunAnimatingAway_showingQS_newHeadsUpAnim_hunTranslatedToBottomOfScreen() {
// Given: the shade is open and scrolled to the bottom to show the QuickSettings
@@ -269,6 +278,27 @@
}
@Test
+ @EnableSceneContainer
+ fun resetViewStates_emptyShadeView_isCenteredVertically_withSceneContainer() {
+ stackScrollAlgorithm.initView(context)
+ hostView.removeAllViews()
+ hostView.addView(emptyShadeView)
+ ambientState.layoutMaxHeight = maxPanelHeight.toInt()
+
+ val stackTop = 200f
+ val stackBottom = 2000f
+ val stackHeight = stackBottom - stackTop
+ ambientState.stackTop = stackTop
+ ambientState.stackCutoff = stackBottom
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ val centeredY = stackTop + stackHeight / 2f - emptyShadeView.height / 2f
+ assertThat(emptyShadeView.viewState.yTranslation).isEqualTo(centeredY)
+ }
+
+ @Test
+ @DisableSceneContainer
fun resetViewStates_emptyShadeView_isCenteredVertically() {
stackScrollAlgorithm.initView(context)
hostView.removeAllViews()
@@ -520,6 +550,7 @@
assertThat((footerView.viewState as FooterViewState).hideContent).isTrue()
}
+ @DisableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
@Test
fun resetViewStates_clearAllInProgress_allRowsRemoved_emptyShade_footerHidden() {
ambientState.isClearAllInProgress = true
@@ -1154,6 +1185,7 @@
assertFalse(stackScrollAlgorithm.shouldHunAppearFromBottom(ambientState, viewState))
}
+
// endregion
private fun createHunViewMock(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index f0bc655..665544d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -61,7 +61,7 @@
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -422,9 +422,17 @@
}
private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
- CastDevice cd = new CastDevice();
- cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED;
- return Collections.singletonList(cd);
+ CastDevice.CastState state = isCasting
+ ? CastDevice.CastState.Connected
+ : CastDevice.CastState.Disconnected;
+ return Collections.singletonList(
+ new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ state,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index a27073c..88ec18d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -18,7 +18,7 @@
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static junit.framework.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
index c44c178..2f81027 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
@@ -18,6 +18,7 @@
import android.content.pm.UserInfo
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
@@ -29,12 +30,14 @@
import junit.framework.Assert
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ManagedProfileControllerImplTest : SysuiTestCase() {
private val mainExecutor: FakeExecutor = FakeExecutor(FakeSystemClock())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index f2f336c..dfee2ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -21,11 +21,14 @@
import android.app.admin.DevicePolicyResourcesManager
import android.content.SharedPreferences
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.telecom.TelecomManager
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
@@ -39,6 +42,7 @@
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.statusbar.policy.CastController
+import com.android.systemui.statusbar.policy.CastDevice
import com.android.systemui.statusbar.policy.DataSaverController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HotspotController
@@ -54,6 +58,7 @@
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.DateFormatUtil
import com.android.systemui.util.time.FakeSystemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -78,6 +83,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@@ -87,6 +93,8 @@
companion object {
private const val ALARM_SLOT = "alarm"
+ private const val CAST_SLOT = "cast"
+ private const val SCREEN_RECORD_SLOT = "screen_record"
private const val CONNECTED_DISPLAY_SLOT = "connected_display"
private const val MANAGED_PROFILE_SLOT = "managed_profile"
}
@@ -271,6 +279,101 @@
verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true)
}
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ fun cast_chipsFlagOff_iconShown() {
+ statusBarPolicy.init()
+ clearInvocations(iconController)
+
+ val callbackCaptor = argumentCaptor<CastController.Callback>()
+ verify(castController).addCallback(callbackCaptor.capture())
+
+ whenever(castController.castDevices)
+ .thenReturn(
+ listOf(
+ CastDevice(
+ "id",
+ "name",
+ "description",
+ CastDevice.CastState.Connected,
+ CastDevice.CastOrigin.MediaProjection,
+ )
+ )
+ )
+ callbackCaptor.firstValue.onCastDevicesChanged()
+
+ verify(iconController).setIconVisibility(CAST_SLOT, true)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ fun cast_chipsFlagOn_noCallbackRegistered() {
+ statusBarPolicy.init()
+
+ verify(castController, never()).addCallback(any())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ fun screenRecord_chipsFlagOff_iconShown_forAllStates() {
+ statusBarPolicy.init()
+ clearInvocations(iconController)
+
+ val callbackCaptor = argumentCaptor<RecordingController.RecordingStateChangeCallback>()
+ verify(recordingController).addCallback(callbackCaptor.capture())
+
+ callbackCaptor.firstValue.onCountdown(3000)
+ testableLooper.processAllMessages()
+ verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, true)
+ clearInvocations(iconController)
+
+ callbackCaptor.firstValue.onCountdownEnd()
+ testableLooper.processAllMessages()
+ verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, false)
+ clearInvocations(iconController)
+
+ callbackCaptor.firstValue.onRecordingStart()
+ testableLooper.processAllMessages()
+ verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, true)
+ clearInvocations(iconController)
+
+ callbackCaptor.firstValue.onRecordingEnd()
+ testableLooper.processAllMessages()
+ verify(iconController).setIconVisibility(SCREEN_RECORD_SLOT, false)
+ clearInvocations(iconController)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ fun screenRecord_chipsFlagOn_noCallbackRegistered() {
+ statusBarPolicy.init()
+
+ verify(recordingController, never()).addCallback(any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+ fun screenRecord_chipsFlagOn_methodsDoNothing() {
+ statusBarPolicy.init()
+ clearInvocations(iconController)
+
+ statusBarPolicy.onCountdown(3000)
+ testableLooper.processAllMessages()
+ verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
+
+ statusBarPolicy.onCountdownEnd()
+ testableLooper.processAllMessages()
+ verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
+
+ statusBarPolicy.onRecordingStart()
+ testableLooper.processAllMessages()
+ verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
+
+ statusBarPolicy.onRecordingEnd()
+ testableLooper.processAllMessages()
+ verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
+ }
+
private fun createAlarmInfo(): AlarmManager.AlarmClockInfo {
return AlarmManager.AlarmClockInfo(10L, null)
}
@@ -315,13 +418,18 @@
private class FakeConnectedDisplayStateProvider : ConnectedDisplayInteractor {
private val flow = MutableSharedFlow<State>()
+
suspend fun emit(value: State) = flow.emit(value)
+
override val connectedDisplayState: Flow<State>
get() = flow
+
override val connectedDisplayAddition: Flow<Unit>
get() = TODO("Not yet implemented")
+
override val pendingDisplay: Flow<PendingDisplay?>
get() = TODO("Not yet implemented")
+
override val concurrentDisplaysInProgress: Flow<Boolean>
get() = TODO("Not yet implemented")
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt
index c4568a9..318656b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt
@@ -21,12 +21,12 @@
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT
-import com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSLUCENT
+import com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index ba38f87..25314f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -27,6 +27,7 @@
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.OnPreDrawListener
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
@@ -54,6 +55,7 @@
import javax.inject.Provider
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.mock
@@ -64,6 +66,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Mock private lateinit var shadeViewController: ShadeViewController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 35888a5..80b9e80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -16,14 +16,18 @@
import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.content.Intent;
+import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.View;
@@ -38,8 +42,10 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentView;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -55,6 +61,8 @@
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
+
+ @Mock private GroupExpansionManager mGroupExpansionManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private com.android.systemui.shade.ShadeController mShadeController;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -77,7 +85,7 @@
mNotificationLockscreenUserManager);
mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
- mock(GroupExpansionManager.class), mNotificationLockscreenUserManager,
+ mGroupExpansionManager, mNotificationLockscreenUserManager,
mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
mActivityStarter, mShadeController,
new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
@@ -103,4 +111,177 @@
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
+
+ @Test
+ public void onMakeExpandedVisibleForRemoteInput_collapsedGroup_expandGroupExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager).toggleGroupExpansion(enrEntry);
+ verify(enr).setUserExpanded(true);
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ }
+
+ @Test
+ public void onMakeExpandedVisibleForRemoteInput_expandedGroup_setUserExpandedTrue() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(true);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ verify(enr).setUserExpanded(true);
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ }
+
+ @Test
+ public void onMakeExpandedVisibleForRemoteInput_nonGroupNotifications_setUserExpandedTrue() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ verify(enr).setUserExpanded(true);
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_notExpandedGroup_toggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager).toggleGroupExpansion(enrEntry);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(privateLayout, never()).setOnExpandedVisibleListener(any());
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_expandedGroup_notToggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(true);
+ when(enr.areChildrenExpanded()).thenReturn(true);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(enrEntry);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(privateLayout, never()).setOnExpandedVisibleListener(any());
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_notExpandedNotification_toggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(false);
+ when(enr.isExpanded()).thenReturn(false);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(enr).toggleExpansionState();
+ verify(privateLayout).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ }
+
+ @Test
+ @EnableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
+ public void onMakeExpandedVisibleForRemoteInput_expandedNotification_notToggleExpansion() {
+ // GIVEN
+ final Runnable onExpandedVisibleRunner = mock(Runnable.class);
+
+ final ExpandableNotificationRow enr = mock(ExpandableNotificationRow.class);
+ final NotificationContentView privateLayout = mock(NotificationContentView.class);
+ final NotificationEntry enrEntry = mock(NotificationEntry.class);
+
+ when(enr.getPrivateLayout()).thenReturn(privateLayout);
+ when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.isChildInGroup()).thenReturn(false);
+ when(enr.isExpanded()).thenReturn(true);
+
+ // WHEN
+ mRemoteInputCallback.onMakeExpandedVisibleForRemoteInput(
+ enr, mock(View.class), false, onExpandedVisibleRunner);
+
+ // THEN
+ verify(enr, never()).toggleExpansionState();
+ verify(privateLayout, never()).setOnExpandedVisibleListener(onExpandedVisibleRunner);
+ verify(enr, never()).setUserExpanded(anyBoolean());
+ verify(mGroupExpansionManager, never()).toggleGroupExpansion(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
index 5a0e13d..2d9880a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone.domain.interactor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -25,8 +26,10 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class LightsOutInteractorTest : SysuiTestCase() {
private val statusBarModeRepository = FakeStatusBarModeRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
index e0f1e1a..219b16f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone.fragment
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -26,9 +27,11 @@
import java.io.PrintWriter
import java.io.StringWriter
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@SmallTest
+@RunWith(AndroidJUnit4::class)
class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() {
private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
index 022b5d2..9f6f51a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
@@ -19,14 +19,17 @@
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP
import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.fragment.StatusBarVisibilityModel.Companion.createDefaultModel
import com.android.systemui.statusbar.phone.fragment.StatusBarVisibilityModel.Companion.createModelFromFlags
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class StatusBarVisibilityModelTest : SysuiTestCase() {
@Test
fun createDefaultModel_everythingEnabled() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 7273d9f..5174ec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -25,11 +25,11 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
@@ -80,7 +80,7 @@
private const val PROC_STATE_INVISIBLE = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
class OngoingCallControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
index ecec124..5ce936d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.statusbar.phone.ongoingcall
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class OngoingCallLoggerTest : SysuiTestCase() {
private val uiEventLoggerFake = UiEventLoggerFake()
private val ongoingCallLogger = OngoingCallLogger(uiEventLoggerFake)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index d6624ca..27c2366 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.statusbar.phone.ongoingcall.data.repository
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class OngoingCallRepositoryTest : SysuiTestCase() {
private val underTest = OngoingCallRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
index 7e523fb..19abbd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone.ui
import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.StatusBarIcon
import com.android.systemui.SysuiTestCase
@@ -32,11 +33,13 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class StatusBarIconControllerImplTest : SysuiTestCase() {
private lateinit var underTest: StatusBarIconControllerImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
index 14bce9c..891ff38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
@@ -25,11 +25,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.StatusBarIcon;
@@ -55,7 +55,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
public class StatusBarIconControllerTest extends LeakCheckedTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 1c9f523..b823333 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
@@ -33,12 +34,14 @@
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@RunWith(AndroidJUnit4::class)
class AirplaneModeViewModelImplTest : SysuiTestCase() {
private lateinit var underTest: AirplaneModeViewModelImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
index 6028712..f9ae5ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.ethernet.domain
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions
import com.android.systemui.res.R
@@ -28,8 +29,10 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class EthernetInteractorTest : SysuiTestCase() {
private val connectivityRepository = FakeConnectivityRepository()
private val underTest = EthernetInteractor(connectivityRepository)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
index 3de50c9..4a2f6f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
@@ -21,15 +21,18 @@
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class SystemUiCarrierConfigTest : SysuiTestCase() {
lateinit var underTest: SystemUiCarrierConfig
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
index 932c4a1..320c148 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -20,6 +20,7 @@
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.systemui.SysuiTestCase
@@ -37,6 +38,7 @@
import org.junit.After
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@@ -45,6 +47,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class CarrierConfigRepositoryTest : SysuiTestCase() {
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index 6785de93..db6f5927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -43,11 +43,12 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.After
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
/**
* Parameterized test for all of the common values of [FakeNetworkEventModel]. This test simply
@@ -56,7 +57,7 @@
*/
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
internal class DemoMobileConnectionParameterizedTest(private val testCase: TestCase) :
SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index a41bc0d..5e0d2fb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -19,6 +19,7 @@
import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
@@ -49,9 +50,11 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
private val dumpManager: DumpManager = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 3c13906..fd4c370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -23,6 +23,7 @@
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -58,6 +59,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -69,6 +71,7 @@
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class FullMobileConnectionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: FullMobileConnectionRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 6d8bf55..8fd0b31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -66,6 +66,7 @@
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
@@ -107,6 +108,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -114,6 +116,7 @@
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MobileConnectionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: MobileConnectionRepositoryImpl
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 1488418..30e96f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -20,6 +20,7 @@
import android.telephony.CellSignalStrength
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
@@ -52,11 +53,13 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MobileIconInteractorTest : SysuiTestCase() {
private lateinit var underTest: MobileIconInteractor
private val mobileMappingsProxy = FakeMobileMappingsProxy()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 58d9ee3..cc0eae7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -21,6 +21,7 @@
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.SysuiTestCase
@@ -50,11 +51,13 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MobileIconsInteractorTest : SysuiTestCase() {
private lateinit var underTest: MobileIconsInteractor
private lateinit var connectivityRepository: FakeConnectivityRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
index 755aaa6..4511be9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui
import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -32,10 +33,12 @@
import java.io.StringWriter
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MobileViewLoggerTest : SysuiTestCase() {
private val buffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10)
private val stringWriter = StringWriter()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
index cdc4733..fc1ea22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
@@ -18,6 +18,7 @@
import android.telephony.TelephonyManager
import android.telephony.satellite.SatelliteManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -41,9 +42,11 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.verify
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DeviceBasedSatelliteRepositorySwitcherTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
index f77fd19..8769389 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.satellite.data.demo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -29,8 +30,10 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DemoDeviceBasedSatelliteRepositoryTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 890a2e4..73ac6e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -32,10 +32,12 @@
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR
import android.telephony.satellite.SatelliteManager.SatelliteException
import android.telephony.satellite.SatelliteModemStateCallback
import android.telephony.satellite.SatelliteProvisionStateCallback
import android.telephony.satellite.SatelliteSupportedStateCallback
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -60,8 +62,10 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -71,6 +75,7 @@
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: DeviceBasedSatelliteRepositoryImpl
@@ -354,13 +359,142 @@
}
@Test
- fun satelliteProvisioned_supported_tracksCallback() =
+ fun satelliteProvisioned_returnsException_defaultsToFalse() =
+ testScope.runTest {
+ // GIVEN satellite is supported on device
+ doAnswer {
+ val callback: OutcomeReceiver<Boolean, SatelliteException> =
+ it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+ callback.onResult(true)
+ }
+ .whenever(satelliteManager)
+ .requestIsSupported(any(), any())
+
+ // GIVEN satellite returns an error when asked if provisioned
+ doAnswer {
+ val receiver = it.arguments[1] as OutcomeReceiver<Boolean, SatelliteException>
+ receiver.onError(SatelliteException(SATELLITE_RESULT_ERROR))
+ null
+ }
+ .whenever(satelliteManager)
+ .requestIsProvisioned(
+ any(),
+ any<OutcomeReceiver<Boolean, SatelliteException>>()
+ )
+
+ // GIVEN we've been up long enough to start querying
+ systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME)
+
+ underTest =
+ DeviceBasedSatelliteRepositoryImpl(
+ Optional.of(satelliteManager),
+ telephonyManager,
+ dispatcher,
+ testScope.backgroundScope,
+ logBuffer = FakeLogBuffer.Factory.create(),
+ verboseLogBuffer = FakeLogBuffer.Factory.create(),
+ systemClock,
+ )
+
+ // WHEN we try to check for provisioned status
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ // THEN well, first we don't throw...
+ // AND THEN we assume that it's not provisioned
+ assertThat(provisioned).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_throwsWhenQuerying_defaultsToFalse() =
+ testScope.runTest {
+ // GIVEN satellite is supported on device
+ doAnswer {
+ val callback: OutcomeReceiver<Boolean, SatelliteException> =
+ it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+ callback.onResult(true)
+ }
+ .whenever(satelliteManager)
+ .requestIsSupported(any(), any())
+
+ // GIVEN satellite throws when asked if provisioned
+ whenever(satelliteManager.requestIsProvisioned(any(), any()))
+ .thenThrow(SecurityException())
+
+ // GIVEN we've been up long enough to start querying
+ systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME)
+
+ underTest =
+ DeviceBasedSatelliteRepositoryImpl(
+ Optional.of(satelliteManager),
+ telephonyManager,
+ dispatcher,
+ testScope.backgroundScope,
+ logBuffer = FakeLogBuffer.Factory.create(),
+ verboseLogBuffer = FakeLogBuffer.Factory.create(),
+ systemClock,
+ )
+
+ // WHEN we try to check for provisioned status
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ // THEN well, first we don't throw...
+ // AND THEN we assume that it's not provisioned
+ assertThat(provisioned).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_provisioned_queriesInitialStateBeforeCallbacks() =
+ testScope.runTest {
+ // GIVEN satellite is supported, and provisioned
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ initialSatelliteIsProvisioned = true,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ runCurrent()
+
+ // THEN the current state is requested
+ verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any())
+
+ // AND the state is correct
+ assertThat(provisioned).isTrue()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_notProvisioned_queriesInitialStateBeforeCallbacks() =
+ testScope.runTest {
+ // GIVEN satellite is supported, and provisioned
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ initialSatelliteIsProvisioned = false,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ runCurrent()
+
+ // THEN the current state is requested
+ verify(satelliteManager, atLeastOnce()).requestIsProvisioned(any(), any())
+
+ // AND the state is correct
+ assertThat(provisioned).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_notInitiallyProvisioned_tracksCallback() =
testScope.runTest {
// GIVEN satellite is not supported
setUpRepo(
uptime = MIN_UPTIME,
satMan = satelliteManager,
satelliteSupported = true,
+ initialSatelliteIsProvisioned = false,
)
val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
@@ -416,6 +550,8 @@
// THEN listeners are re-registered
verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any())
+ // AND the state is queried again
+ verify(satelliteManager, times(2)).requestIsProvisioned(any(), any())
}
@Test
@@ -632,6 +768,7 @@
uptime: Long = MIN_UPTIME,
satMan: SatelliteManager? = satelliteManager,
satelliteSupported: Boolean = true,
+ initialSatelliteIsProvisioned: Boolean = true,
) {
doAnswer {
val callback: OutcomeReceiver<Boolean, SatelliteException> =
@@ -641,6 +778,14 @@
.whenever(satelliteManager)
.requestIsSupported(any(), any())
+ doAnswer {
+ val callback: OutcomeReceiver<Boolean, SatelliteException> =
+ it.getArgument(1) as OutcomeReceiver<Boolean, SatelliteException>
+ callback.onResult(initialSatelliteIsProvisioned)
+ }
+ .whenever(satelliteManager)
+ .requestIsProvisioned(any(), any())
+
systemClock.setUptimeMillis(Process.getStartUptimeMillis() + uptime)
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index 2e5ebb3..cd0390e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.telephony.flags.Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG
import com.android.systemui.SysuiTestCase
@@ -38,8 +39,10 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DeviceBasedSatelliteInteractorTest : SysuiTestCase() {
private lateinit var underTest: DeviceBasedSatelliteInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index c39e301..64b07fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
@@ -41,9 +42,11 @@
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
private lateinit var underTest: DeviceBasedSatelliteViewModel
private lateinit var interactor: DeviceBasedSatelliteInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt
index c43778a..4c2bb7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModelTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.statusbar.pipeline.shared.data.model
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogMessageImpl
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class DefaultConnectionModelTest : SysuiTestCase() {
@Test
fun messageInitializerAndPrinter_isValidatedFalse_hasCorrectInfo() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
index fa4e91b..f486787 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -25,6 +25,7 @@
import android.net.vcn.VcnTransportInfo
import android.net.wifi.WifiInfo
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -50,6 +51,7 @@
import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -57,6 +59,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ConnectivityRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: ConnectivityRepositoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
index 028a58c..c3ad0f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.pipeline.shared.ui.view
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
@@ -30,7 +30,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class ModernStatusBarViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt
index ca9df57..a6cdfe2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconViewTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.pipeline.shared.ui.view
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.StatusBarIconView
@@ -32,7 +32,7 @@
* method.
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class SingleBindableStatusBarIconViewTest : SysuiTestCase() {
private lateinit var binding: SingleBindableStatusBarIconViewBinding
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 92de866..b4c6106 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -60,9 +61,11 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
private val kosmos =
Kosmos().also {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 0cb3329..2238bff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.SysuiTestCase
@@ -55,8 +56,10 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class InternetTileViewModelTest : SysuiTestCase() {
private lateinit var underTest: InternetTileViewModel
private lateinit var mobileIconsInteractor: MobileIconsInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
index ba035be..eb6b068 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
@@ -18,6 +18,7 @@
import android.net.wifi.WifiManager.UNKNOWN_SSID
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableRowLogger
@@ -25,8 +26,10 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class WifiNetworkModelTest : SysuiTestCase() {
@Test
fun active_levelsInValidRange_noException() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 9ab64d65..93071bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -43,6 +43,7 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.BluetoothEventManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index 68c1b8d..59b20c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -9,12 +9,13 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.pm.PackageManager;
import android.media.MediaRouter;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,7 +34,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class CastControllerImplTest extends SysuiTestCase {
@@ -52,8 +53,12 @@
mContext.addMockSystemService(MediaRouter.class, mMediaRouter);
mContext.addMockSystemService(MediaProjectionManager.class, mMediaProjectionManager);
when(mMediaProjectionManager.getActiveProjectionInfo()).thenReturn(mProjection);
+ when(mProjection.getPackageName()).thenReturn("fake.package");
- mController = new CastControllerImpl(mContext, mock(DumpManager.class));
+ mController = new CastControllerImpl(
+ mContext,
+ mock(PackageManager.class),
+ mock(DumpManager.class));
}
@Test
@@ -148,16 +153,26 @@
@Test
public void hasConnectedCastDevice_connected() {
- CastController.CastDevice castDevice = new CastController.CastDevice();
- castDevice.state = CastController.CastDevice.STATE_CONNECTED;
+ CastDevice castDevice = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connected,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null);
mController.startCasting(castDevice);
assertTrue(mController.hasConnectedCastDevice());
}
@Test
public void hasConnectedCastDevice_notConnected() {
- CastController.CastDevice castDevice = new CastController.CastDevice();
- castDevice.state = CastController.CastDevice.STATE_CONNECTING;
+ CastDevice castDevice = new CastDevice(
+ "id",
+ /* name= */ null,
+ /* description= */ null,
+ /* state= */ CastDevice.CastState.Connecting,
+ /* origin= */ CastDevice.CastOrigin.MediaProjection,
+ /* tag= */ null);
mController.startCasting(castDevice);
assertTrue(mController.hasConnectedCastDevice());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
new file mode 100644
index 0000000..03ad66c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
@@ -0,0 +1,441 @@
+/*
+ * 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.policy
+
+import android.Manifest
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.media.MediaRouter
+import android.media.projection.MediaProjectionInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.CastDevice.Companion.toCastDevice
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.doAnswer
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class CastDeviceTest : SysuiTestCase() {
+ private val mockAppInfo =
+ mock<ApplicationInfo>().apply { whenever(this.loadLabel(any())).thenReturn("") }
+
+ private val packageManager =
+ mock<PackageManager>().apply {
+ whenever(
+ this.checkPermission(
+ Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+ HEADLESS_REMOTE_PACKAGE,
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+ whenever(
+ this.checkPermission(
+ Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+ NORMAL_PACKAGE,
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_DENIED)
+
+ doAnswer {
+ // See Utils.isHeadlessRemoteDisplayProvider
+ if ((it.arguments[0] as Intent).`package` == HEADLESS_REMOTE_PACKAGE) {
+ emptyList()
+ } else {
+ listOf(mock<ResolveInfo>())
+ }
+ }
+ .whenever(this)
+ .queryIntentActivities(any(), ArgumentMatchers.anyInt())
+
+ whenever(this.getApplicationInfo(any<String>(), any<Int>())).thenReturn(mockAppInfo)
+ }
+
+ @Test
+ fun isCasting_disconnected_false() {
+ val device =
+ CastDevice(
+ id = "id",
+ name = "name",
+ state = CastDevice.CastState.Disconnected,
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+
+ assertThat(device.isCasting).isFalse()
+ }
+
+ @Test
+ fun isCasting_connecting_true() {
+ val device =
+ CastDevice(
+ id = "id",
+ name = "name",
+ state = CastDevice.CastState.Connecting,
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+
+ assertThat(device.isCasting).isTrue()
+ }
+
+ @Test
+ fun isCasting_connected_true() {
+ val device =
+ CastDevice(
+ id = "id",
+ name = "name",
+ state = CastDevice.CastState.Connected,
+ origin = CastDevice.CastOrigin.MediaRouter,
+ )
+
+ assertThat(device.isCasting).isTrue()
+ }
+
+ @Test
+ fun routeToCastDevice_statusNone_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_NONE)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusNotAvailable_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusScanning_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_SCANNING)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusAvailable_isSelectedFalse_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_AVAILABLE)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusAvailable_isSelectedTrue_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_AVAILABLE)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusInUse_isSelectedFalse_stateDisconnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_IN_USE)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusInUse_isSelectedTrue_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_IN_USE)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnecting_isSelectedFalse_stateConnecting() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTING)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connecting)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnecting_isSelectedTrue_stateConnecting() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTING)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connecting)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnected_isSelectedFalse_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTED)
+ whenever(this.isSelected).thenReturn(false)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_statusConnected_isSelectedTrue_stateConnected() {
+ val route =
+ mockRouteInfo().apply {
+ whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTED)
+ whenever(this.isSelected).thenReturn(true)
+ }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun routeToCastDevice_tagIsStringType_idMatchesTag() {
+ val route = mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn("FakeTag") }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.id).isEqualTo("FakeTag")
+ }
+
+ @Test
+ fun routeToCastDevice_tagIsOtherType_idMatchesTag() {
+ val tag = listOf("tag1", "tag2")
+ val route = mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn(tag) }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.id).isEqualTo(tag.toString())
+ }
+
+ @Test
+ fun routeToCastDevice_nameMatchesName() {
+ val route = mockRouteInfo().apply { whenever(this.getName(context)).thenReturn("FakeName") }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.name).isEqualTo("FakeName")
+ }
+
+ @Test
+ fun routeToCastDevice_descriptionMatchesDescription() {
+ val route =
+ mockRouteInfo().apply { whenever(this.description).thenReturn("FakeDescription") }
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.description).isEqualTo("FakeDescription")
+ }
+
+ @Test
+ fun routeToCastDevice_tagIsRoute() {
+ val route = mockRouteInfo()
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.tag).isEqualTo(route)
+ }
+
+ @Test
+ fun routeToCastDevice_originIsMediaRouter() {
+ val route = mockRouteInfo()
+
+ val device = route.toCastDevice(context)
+
+ assertThat(device.origin).isEqualTo(CastDevice.CastOrigin.MediaRouter)
+ }
+
+ @Test
+ fun routeToCastDevice_nullValues_ok() {
+ val device = mockRouteInfo().toCastDevice(context)
+
+ assertThat(device.name).isNull()
+ assertThat(device.description).isNull()
+ }
+
+ @Test
+ fun projectionToCastDevice_idMatchesPackage() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn("fake.package")
+ }
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.id).isEqualTo("fake.package")
+ }
+
+ @Test
+ fun projectionToCastDevice_name_packageIsHeadlessRemote_isEmpty() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(HEADLESS_REMOTE_PACKAGE)
+ }
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEmpty()
+ }
+
+ @Test
+ fun projectionToCastDevice_name_packageMissingFromPackageManager_isPackageName() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+ }
+
+ whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+ .thenThrow(PackageManager.NameNotFoundException())
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEqualTo(NORMAL_PACKAGE)
+ }
+
+ @Test
+ fun projectionToCastDevice_name_nameFromPackageManagerEmpty_isPackageName() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+ }
+
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(packageManager)).thenReturn("")
+ whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+ .thenReturn(appInfo)
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEqualTo(NORMAL_PACKAGE)
+ }
+
+ @Test
+ fun projectionToCastDevice_name_packageManagerHasName_isName() {
+ val projection =
+ mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+ }
+
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(packageManager)).thenReturn("Valid App Name")
+ whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+ .thenReturn(appInfo)
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.name).isEqualTo("Valid App Name")
+ }
+
+ @Test
+ fun projectionToCastDevice_descriptionIsCasting() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.description).isEqualTo(context.getString(R.string.quick_settings_casting))
+ }
+
+ @Test
+ fun projectionToCastDevice_stateIsConnected() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+ }
+
+ @Test
+ fun projectionToCastDevice_tagIsProjection() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.tag).isEqualTo(projection)
+ }
+
+ @Test
+ fun projectionToCastDevice_originIsMediaProjection() {
+ val projection = mockProjectionInfo()
+
+ val device = projection.toCastDevice(context, packageManager)
+
+ assertThat(device.origin).isEqualTo(CastDevice.CastOrigin.MediaProjection)
+ }
+
+ private fun mockRouteInfo(): MediaRouter.RouteInfo {
+ return mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn(Any()) }
+ }
+
+ private fun mockProjectionInfo(): MediaProjectionInfo {
+ return mock<MediaProjectionInfo>().apply {
+ whenever(this.packageName).thenReturn("fake.package")
+ }
+ }
+
+ private companion object {
+ const val HEADLESS_REMOTE_PACKAGE = "headless.remote.package"
+ const val NORMAL_PACKAGE = "normal.package"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
index 22c72cc..7549a7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
@@ -16,12 +16,12 @@
package com.android.systemui.statusbar.policy
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View.MeasureSpec.UNSPECIFIED
import android.view.View.MeasureSpec.makeMeasureSpec
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
@@ -31,7 +31,7 @@
import org.junit.Test
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ClockTest : SysuiTestCase() {
private lateinit var clockView: Clock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
index c606511..f871d27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
@@ -18,9 +18,9 @@
import android.hardware.devicestate.DeviceState
import android.hardware.devicestate.DeviceStateManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableResources
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
@@ -44,7 +44,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class DevicePostureControllerImplTest : SysuiTestCase() {
private val useBaseStateDeviceState = SUPPORTED_POSTURES_SIZE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
index 31bd57e..649a4ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -18,8 +18,8 @@
import android.os.Handler
import android.provider.Settings
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class DeviceProvisionedControllerImplTest : SysuiTestCase() {
@@ -246,4 +246,4 @@
`when`(userTracker.userId).thenReturn(toUser)
userTrackerCallbackCaptor.value.onUserChanged(toUser, mContext)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index 683136d..f5efd6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -23,10 +23,10 @@
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -51,7 +51,7 @@
import java.util.Map;
import java.util.function.Consumer;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class ExtensionControllerImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index 784fb71..3721b0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -33,9 +33,9 @@
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.UserManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -57,7 +57,7 @@
import java.util.concurrent.Executor;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class HotspotControllerImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index 1250228..b7a3515 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -16,12 +16,12 @@
package com.android.systemui.statusbar.policy
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.res.R
@@ -45,7 +45,7 @@
@SmallTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
@Mock
private lateinit var userSwitcherController: UserSwitcherController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index 3a086ae..aed9af6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -25,9 +25,9 @@
import static org.mockito.Mockito.when;
import android.hardware.biometrics.BiometricSourceType;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
@@ -54,7 +54,7 @@
@SmallTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class KeyguardStateControllerTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
index 635d63e..750f0c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
@@ -15,8 +15,8 @@
import android.app.StatusBarManager
import android.content.res.Configuration
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
@@ -35,7 +35,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class RemoteInputQuickSettingsDisablerTest : SysuiTestCase() {
@@ -134,4 +134,4 @@
private fun shouldDisableQs(state: Int): Boolean {
return state and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 70afbd8..ffe7750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -247,7 +247,7 @@
ExpandableNotificationRow row = helper.createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
- view.addOnVisibilityChangedListener(null);
+ view.setOnVisibilityChangedListener(null);
view.setVisibility(View.INVISIBLE);
view.setVisibility(View.VISIBLE);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index a9681e0..36e3052 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -22,10 +22,10 @@
import android.app.RemoteInput;
import android.provider.DeviceConfig;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -39,7 +39,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@SmallTest
public class SmartReplyConstantsTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index ddd29c3..637a0f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -27,10 +27,10 @@
import android.os.Handler;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -51,7 +51,7 @@
import java.util.concurrent.atomic.AtomicInteger;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class ZenModeControllerImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt
index 6f40f15..94f0d19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepositoryImplTest.kt
@@ -15,6 +15,7 @@
package com.android.systemui.statusbar.policy.bluetooth
import android.bluetooth.BluetoothProfile
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothAdapter
@@ -30,11 +31,13 @@
import kotlinx.coroutines.test.TestScope
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class BluetoothRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: BluetoothRepositoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt
deleted file mode 100644
index 004f679..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryImplTest.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.statusbar.policy.data.repository
-
-import android.app.NotificationManager
-import android.provider.Settings
-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.statusbar.policy.ZenModeController
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class ZenModeRepositoryImplTest : SysuiTestCase() {
- @Mock lateinit var zenModeController: ZenModeController
-
- lateinit var underTest: ZenModeRepositoryImpl
-
- private val testPolicy = NotificationManager.Policy(0, 1, 0)
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- underTest = ZenModeRepositoryImpl(zenModeController)
- }
-
- @Test
- fun zenMode_reflectsCurrentControllerState() = runTest {
- whenever(zenModeController.zen).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
- val zenMode by collectLastValue(underTest.zenMode)
- assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
- }
-
- @Test
- fun zenMode_updatesWhenControllerStateChanges() = runTest {
- val zenMode by collectLastValue(underTest.zenMode)
- runCurrent()
- whenever(zenModeController.zen).thenReturn(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- withArgCaptor { Mockito.verify(zenModeController).addCallback(capture()) }
- .onZenChanged(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- assertThat(zenMode).isEqualTo(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
- }
-
- @Test
- fun policy_reflectsCurrentControllerState() {
- runTest {
- whenever(zenModeController.consolidatedPolicy).thenReturn(testPolicy)
- val policy by collectLastValue(underTest.consolidatedNotificationPolicy)
- assertThat(policy).isEqualTo(testPolicy)
- }
- }
-
- @Test
- fun policy_updatesWhenControllerStateChanges() = runTest {
- val policy by collectLastValue(underTest.consolidatedNotificationPolicy)
- runCurrent()
- whenever(zenModeController.consolidatedPolicy).thenReturn(testPolicy)
- withArgCaptor { Mockito.verify(zenModeController).addCallback(capture()) }
- .onConsolidatedPolicyChanged(testPolicy)
- assertThat(policy).isEqualTo(testPolicy)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
deleted file mode 100644
index dbb1062..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy.domain.interactor
-
-import android.app.NotificationManager.Policy
-import android.provider.Settings
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.collectLastValue
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepository
-import com.android.systemui.user.domain.UserDomainLayerModule
-import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
-import org.junit.Test
-
-@SmallTest
-class ZenModeInteractorTest : SysuiTestCase() {
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- UserDomainLayerModule::class,
- ]
- )
- interface TestComponent : SysUITestComponent<ZenModeInteractor> {
-
- val repository: FakeZenModeRepository
-
- @Component.Factory
- interface Factory {
- fun create(@BindsInstance test: SysuiTestCase): TestComponent
- }
- }
-
- private val testComponent: TestComponent =
- DaggerZenModeInteractorTest_TestComponent.factory().create(test = this)
-
- @Test
- fun testIsZenModeEnabled_off() =
- testComponent.runTest {
- val enabled by collectLastValue(underTest.isZenModeEnabled)
-
- repository.zenMode.value = Settings.Global.ZEN_MODE_OFF
- runCurrent()
-
- assertThat(enabled).isFalse()
- }
-
- @Test
- fun testIsZenModeEnabled_alarms() =
- testComponent.runTest {
- val enabled by collectLastValue(underTest.isZenModeEnabled)
-
- repository.zenMode.value = Settings.Global.ZEN_MODE_ALARMS
- runCurrent()
-
- assertThat(enabled).isTrue()
- }
-
- @Test
- fun testIsZenModeEnabled_importantInterruptions() =
- testComponent.runTest {
- val enabled by collectLastValue(underTest.isZenModeEnabled)
-
- repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- runCurrent()
-
- assertThat(enabled).isTrue()
- }
-
- @Test
- fun testIsZenModeEnabled_noInterruptions() =
- testComponent.runTest {
- val enabled by collectLastValue(underTest.isZenModeEnabled)
-
- repository.zenMode.value = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
- runCurrent()
-
- assertThat(enabled).isTrue()
- }
-
- @Test
- fun testIsZenModeEnabled_unknown() =
- testComponent.runTest {
- val enabled by collectLastValue(underTest.isZenModeEnabled)
-
- repository.zenMode.value = 4 // this should fail if we ever add another zen mode type
- runCurrent()
-
- assertThat(enabled).isFalse()
- }
-
- @Test
- fun testAreNotificationsHiddenInShade_noPolicy() =
- testComponent.runTest {
- val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
-
- repository.consolidatedNotificationPolicy.value = null
- repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- runCurrent()
-
- assertThat(hidden).isFalse()
- }
-
- @Test
- fun testAreNotificationsHiddenInShade_zenOffShadeSuppressed() =
- testComponent.runTest {
- val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
-
- repository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
- repository.zenMode.value = Settings.Global.ZEN_MODE_OFF
- runCurrent()
-
- assertThat(hidden).isFalse()
- }
-
- @Test
- fun testAreNotificationsHiddenInShade_zenOnShadeNotSuppressed() =
- testComponent.runTest {
- val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
-
- repository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_STATUS_BAR)
- repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- runCurrent()
-
- assertThat(hidden).isFalse()
- }
-
- @Test
- fun testAreNotificationsHiddenInShade_zenOnShadeSuppressed() =
- testComponent.runTest {
- val hidden by collectLastValue(underTest.areNotificationsHiddenInShade)
-
- repository.setSuppressedVisualEffects(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST)
- repository.zenMode.value = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- runCurrent()
-
- assertThat(hidden).isTrue()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
index 8576d4f..03a25e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
@@ -21,18 +21,21 @@
import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class StatusBarWindowStateControllerTest : SysuiTestCase() {
private lateinit var controller: StatusBarWindowStateController
private lateinit var callback: CommandQueue.Callbacks
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
index 5b9db4b..5603ff0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
@@ -31,8 +31,8 @@
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
import com.android.systemui.InstanceIdSequenceFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
@@ -109,6 +109,14 @@
}
@Test
+ fun updateBatteryState_capacityNaN_cancelsNotification() {
+ stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(Float.NaN))
+
+ verify(notificationManager, times(1)).cancel(R.string.stylus_battery_low_percentage)
+ verifyNoMoreInteractions(notificationManager)
+ }
+
+ @Test
fun updateBatteryState_capacityBelowThreshold_notifies() {
stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
index 16132ba..32ef501 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
@@ -18,8 +18,8 @@
import android.graphics.Color
import android.graphics.Paint
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
@@ -31,7 +31,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class GlowBoxEffectTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
index 41d7fd5..67a4219 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
@@ -18,8 +18,8 @@
import android.graphics.Paint
import android.graphics.RenderEffect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class LoadingEffectTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
index 0d19ab1..a5afe1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.surfaceeffects.ripple.MultiRippleController.Companion.MAX_RIPPLE_NUMBER
@@ -29,7 +29,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MultiRippleControllerTest : SysuiTestCase() {
private lateinit var multiRippleController: MultiRippleController
private lateinit var multiRippleView: MultiRippleView
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
index 74ed7fb..8dafa82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
-import android.testing.AndroidTestingRunner
import androidx.core.graphics.ColorUtils
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class RippleAnimationTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt
index 89cc18cc..2fee2a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleShaderTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.surfaceeffects.ripple
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -24,7 +24,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class RippleShaderTest : SysuiTestCase() {
private lateinit var rippleShader: RippleShader
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
index 1e5ab7e..18644ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.surfaceeffects.ripple
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
@@ -23,7 +23,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class RippleViewTest : SysuiTestCase() {
private lateinit var rippleView: RippleView
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt
index f1fadf6..fbd3eae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SolidColorShaderTest.kt
@@ -16,14 +16,14 @@
package com.android.systemui.surfaceeffects.shaders
import android.graphics.Color
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SolidColorShaderTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt
index 64ea6a6..d32bffb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/shaders/SparkleShaderTest.kt
@@ -16,14 +16,14 @@
package com.android.systemui.surfaceeffects.shaders
import android.graphics.Color
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SparkleShaderTest : SysuiTestCase() {
private lateinit var sparkleShader: SparkleShader
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
index 08b49f0..4ba6438 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.surfaceeffects.turbulencenoise
import android.graphics.Color
-import android.testing.AndroidTestingRunner
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController.Companion.AnimationState.EASE_IN
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class TurbulenceNoiseControllerTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
// FakeExecutor is needed to run animator.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt
index e62ca64..286f017 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShaderTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.surfaceeffects.turbulencenoise
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class TurbulenceNoiseShaderTest : SysuiTestCase() {
private lateinit var turbulenceNoiseShader: TurbulenceNoiseShader
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
index 953071c..e09d4ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.surfaceeffects.turbulencenoise
import android.graphics.Color
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class TurbulenceNoiseViewTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index ae4dfbd..bb6ba46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -23,6 +23,7 @@
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
@@ -44,6 +45,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
@@ -53,6 +55,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TemporaryViewDisplayControllerTest : SysuiTestCase() {
private lateinit var underTest: TestController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
index 38c1a78..1dfd934 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.temporarydisplay
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
@@ -28,9 +29,11 @@
import java.io.StringWriter
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TemporaryViewLoggerTest : SysuiTestCase() {
private lateinit var buffer: LogBuffer
private lateinit var logger: TemporaryViewLogger<TemporaryViewInfo>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
index f707a8da..281ecfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.temporarydisplay
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
@@ -23,8 +24,10 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TemporaryViewUiEventLoggerTest : SysuiTestCase() {
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var logger: TemporaryViewUiEventLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
index 7586fe4..a230f06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TouchableRegionViewControllerTest.kt
@@ -19,12 +19,14 @@
import android.graphics.Rect
import android.view.View
import android.view.ViewTreeObserver
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -32,6 +34,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
+@RunWith(AndroidJUnit4::class)
class TouchableRegionViewControllerTest : SysuiTestCase() {
@Mock private lateinit var view: View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
index c539246..75d031d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt
@@ -19,6 +19,7 @@
import android.graphics.Rect
import android.view.MotionEvent
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
import com.android.systemui.SysuiTestCase
@@ -29,8 +30,10 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
+@RunWith(AndroidJUnit4::class)
class SwipeChipbarAwayGestureHandlerTest : SysuiTestCase() {
private lateinit var underTest: SwipeChipbarAwayGestureHandler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModelTest.kt
new file mode 100644
index 0000000..2300a1f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModelTest.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.touchpad.tutorial.ui
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.model.sysUiState
+import com.android.systemui.settings.displayTracker
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TouchpadTutorialViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = TestScope()
+ private val sysUiState = kosmos.sysUiState
+ private val viewModel =
+ TouchpadTutorialViewModel(
+ TouchpadGesturesInteractor(sysUiState, kosmos.displayTracker, testScope.backgroundScope)
+ )
+
+ @Test
+ fun sysUiStateFlag_disabledByDefault() =
+ testScope.runTest {
+ assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isFalse()
+ }
+
+ @Test
+ fun sysUiStateFlag_enabledAfterTutorialOpened() =
+ testScope.runTest {
+ viewModel.onOpened()
+
+ assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isTrue()
+ }
+
+ @Test
+ fun sysUiStateFlag_disabledAfterTutorialClosed() =
+ testScope.runTest {
+ viewModel.onOpened()
+ viewModel.onClosed()
+
+ assertThat(isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED)).isFalse()
+ }
+
+ private fun TestScope.isFlagEnabled(@SystemUiStateFlags flag: Long): Boolean {
+ // sysui state is changed on background scope so let's make sure it's executed
+ runCurrent()
+ return sysUiState.isFlagEnabled(flag)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java b/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java
index 683397e..bb7b31b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/tuner/TunablePaddingTest.java
@@ -26,14 +26,17 @@
import android.view.View;
import android.view.WindowManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
+@RunWith(AndroidJUnit4.class)
public class TunablePaddingTest extends LeakCheckedTest {
private static final String KEY = "KEY";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 2cdc8d8..5d850d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.content.res.Resources
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
@@ -65,7 +65,7 @@
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index e1797c4..c29b86c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -19,9 +19,9 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
import android.os.PowerManager
-import android.testing.AndroidTestingRunner
import android.view.ViewGroup
import android.view.ViewTreeObserver
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
@@ -56,7 +56,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FoldAodAnimationControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
index dddc712..266a60d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
@@ -18,11 +18,14 @@
import android.os.PowerManager
import android.os.SystemProperties
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.foldables.FoldLockSettingAvailabilityProvider
+import com.android.internal.jank.Cuj.CUJ_FOLD_ANIM
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.kosmos.Kosmos
@@ -32,27 +35,34 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
+import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.atLeast
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
+ @get:Rule val animatorTestRule = AnimatorTestRule(this)
+
private val kosmos = Kosmos()
private val testScope: TestScope = kosmos.testScope
private val fakeDeviceStateRepository = kosmos.fakeDeviceStateRepository
@@ -63,6 +73,8 @@
private val mockFoldLockSettingAvailabilityProvider =
mock<FoldLockSettingAvailabilityProvider>()
private val onOverlayReady = mock<Runnable>()
+ private val mockJankMonitor = mock<InteractionJankMonitor>()
+ private val mockScrimView = mock<LightRevealScrim>()
private lateinit var foldLightRevealAnimation: FoldLightRevealOverlayAnimation
@Before
@@ -71,6 +83,8 @@
whenever(mockFoldLockSettingAvailabilityProvider.isFoldLockBehaviorAvailable)
.thenReturn(true)
fakeAnimationStatusRepository.onAnimationStatusChanged(true)
+ whenever(mockFullScreenController.scrimView).thenReturn(mockScrimView)
+ whenever(mockJankMonitor.begin(any(), eq(CUJ_FOLD_ANIM))).thenReturn(true)
foldLightRevealAnimation =
FoldLightRevealOverlayAnimation(
@@ -80,7 +94,8 @@
testScope.backgroundScope,
fakeAnimationStatusRepository,
mockControllerFactory,
- mockFoldLockSettingAvailabilityProvider
+ mockFoldLockSettingAvailabilityProvider,
+ mockJankMonitor
)
foldLightRevealAnimation.init()
}
@@ -99,9 +114,9 @@
testScope.runTest {
foldDeviceToScreenOff()
emitLastWakefulnessEventStartingToSleep()
- advanceTimeBy(SHORT_DELAY_MS)
+ advanceTime(SHORT_DELAY_MS)
turnScreenOn()
- advanceTimeBy(ANIMATION_DURATION)
+ advanceTime(ANIMATION_DURATION)
verifyFoldAnimationDidNotPlay()
}
@@ -111,8 +126,8 @@
testScope.runTest {
foldDeviceToScreenOff()
emitLastWakefulnessEventStartingToSleep()
- advanceTimeBy(SHORT_DELAY_MS)
- advanceTimeBy(ANIMATION_DURATION)
+ advanceTime(SHORT_DELAY_MS)
+ advanceTime(ANIMATION_DURATION)
verifyFoldAnimationDidNotPlay()
}
@@ -122,10 +137,10 @@
testScope.runTest {
foldDeviceToScreenOff()
foldLightRevealAnimation.onScreenTurningOn {}
- advanceTimeBy(WAIT_FOR_ANIMATION_TIMEOUT_MS)
+ advanceTime(WAIT_FOR_ANIMATION_TIMEOUT_MS)
powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)
- advanceTimeBy(SHORT_DELAY_MS)
- advanceTimeBy(ANIMATION_DURATION)
+ advanceTime(SHORT_DELAY_MS)
+ advanceTime(ANIMATION_DURATION)
verifyFoldAnimationDidNotPlay()
}
@@ -135,10 +150,12 @@
testScope.runTest {
foldDeviceToScreenOff()
foldLightRevealAnimation.onScreenTurningOn {}
- advanceTimeBy(SHORT_DELAY_MS)
- advanceTimeBy(ANIMATION_DURATION)
+ advanceTime(SHORT_DELAY_MS)
+ clearInvocations(mockFullScreenController)
+
+ // Unfold the device
fakeDeviceStateRepository.emit(DeviceState.HALF_FOLDED)
- advanceTimeBy(SHORT_DELAY_MS)
+ advanceTime(SHORT_DELAY_MS)
powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)
verifyOverlayWasRemoved()
@@ -149,12 +166,38 @@
testScope.runTest {
foldDeviceToScreenOff()
turnScreenOn()
- advanceTimeBy(ANIMATION_DURATION)
+ clearInvocations(mockFullScreenController)
+ advanceTime(ANIMATION_DURATION)
verifyOverlayWasRemoved()
}
@Test
+ fun foldToScreenOn_jankCujIsStarted() =
+ testScope.runTest {
+ foldDeviceToScreenOff()
+ turnScreenOn()
+ // Cuj has started but not ended
+ verify(mockJankMonitor, times(1)).begin(any(), eq(CUJ_FOLD_ANIM))
+ verify(mockJankMonitor, never()).end(eq(CUJ_FOLD_ANIM))
+ }
+
+ @Test
+ fun foldToScreenOn_animationFinished_jankCujIsFinished() =
+ testScope.runTest {
+ foldDeviceToScreenOff()
+ turnScreenOn()
+
+ advanceTime(ANIMATION_DURATION)
+ verify(mockJankMonitor, times(1)).end(eq(CUJ_FOLD_ANIM))
+ }
+
+ private fun TestScope.advanceTime(timeMs: Long) {
+ animatorTestRule.advanceTimeBy(timeMs)
+ advanceTimeBy(timeMs)
+ }
+
+ @Test
fun unfold_immediatelyRunRunnable() =
testScope.runTest {
foldLightRevealAnimation.onScreenTurningOn(onOverlayReady)
@@ -165,18 +208,18 @@
private suspend fun TestScope.foldDeviceToScreenOff() {
fakeDeviceStateRepository.emit(DeviceState.HALF_FOLDED)
powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)
- advanceTimeBy(SHORT_DELAY_MS)
+ advanceTime(SHORT_DELAY_MS)
fakeDeviceStateRepository.emit(DeviceState.FOLDED)
- advanceTimeBy(SHORT_DELAY_MS)
+ advanceTime(SHORT_DELAY_MS)
powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_OFF)
- advanceTimeBy(SHORT_DELAY_MS)
+ advanceTime(SHORT_DELAY_MS)
}
private fun TestScope.turnScreenOn() {
foldLightRevealAnimation.onScreenTurningOn {}
- advanceTimeBy(SHORT_DELAY_MS)
+ advanceTime(SHORT_DELAY_MS)
powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)
- advanceTimeBy(SHORT_DELAY_MS)
+ advanceTime(SHORT_DELAY_MS)
}
private fun emitLastWakefulnessEventStartingToSleep() =
@@ -195,6 +238,7 @@
const val WAIT_FOR_ANIMATION_TIMEOUT_MS = 2000L
val ANIMATION_DURATION: Long
get() = SystemProperties.getLong("persist.fold_animation_duration", 200L)
+
const val SHORT_DELAY_MS = 50L
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
index 39e4e64..459b3e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.unfold
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.FoldStateLoggingProvider.FoldStateLoggingListener
@@ -34,7 +34,7 @@
import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FoldStateLoggingProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
index ab779a7..448b63e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.unfold
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -38,7 +38,7 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FoldStateRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
index 06f1a88..208e39c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
@@ -18,7 +18,7 @@
import android.os.VibrationAttributes
import android.os.VibrationEffect
import android.os.vibrator
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.testKosmos
@@ -30,7 +30,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class UnfoldHapticsPlayerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index 2955384..1e5929d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -19,7 +19,7 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
import android.provider.Settings
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
@@ -39,7 +39,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class UnfoldLatencyTrackerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
index 0c452eb..b5282eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
@@ -1,6 +1,6 @@
package com.android.systemui.unfold
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.WallpaperController
@@ -13,7 +13,7 @@
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class UnfoldTransitionWallpaperControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
index 3c53997..b48a6da3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
@@ -14,7 +14,7 @@
*/
package com.android.systemui.unfold.config
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
* Internal Android resource constants are not available in public APIs,
* so we can't use them there directly.
*/
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ResourceUnfoldTransitionConfigTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
index e5f619b..61f0abb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
@@ -19,8 +19,8 @@
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.FakeUnfoldTransitionProvider
@@ -29,7 +29,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
index 14fb054..ef2d4ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
@@ -17,7 +17,7 @@
import android.os.Handler
import android.os.HandlerThread
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -29,11 +29,17 @@
import com.android.systemui.unfold.util.TestFoldStateProvider
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+/**
+ * This test class tests [PhysicsBasedUnfoldTransitionProgressProvider] in a more E2E
+ * fashion, it uses real handler thread and timings, so it might be perceptible to more flakiness
+ * compared to the other unit tests that do not perform real multithreaded interactions.
+ */
+@RunWith(AndroidJUnit4::class)
@SmallTest
class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
@@ -44,8 +50,8 @@
mock<UnfoldFrameCallbackScheduler.Factory>().apply {
whenever(create()).then { UnfoldFrameCallbackScheduler() }
}
- private val mockBgHandler = mock<Handler>()
- private val fakeHandler = Handler(HandlerThread("UnfoldBg").apply { start() }.looper)
+ private val handlerThread = HandlerThread("UnfoldBg").apply { start() }
+ private val bgHandler = Handler(handlerThread.looper)
@Before
fun setUp() {
@@ -54,20 +60,26 @@
context,
schedulerFactory,
foldStateProvider = foldStateProvider,
- progressHandler = fakeHandler
+ progressHandler = bgHandler
)
progressProvider.addCallback(listener)
}
+ @After
+ fun after() {
+ handlerThread.quit()
+ }
+
@Test
fun testUnfold_emitsIncreasingTransitionEvents() {
runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
- { foldStateProvider.sendUnfoldedScreenAvailable() },
- { foldStateProvider.sendHingeAngleUpdate(90f) },
- { foldStateProvider.sendHingeAngleUpdate(180f) },
- { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) },
+ { foldStateProvider.sendUnfoldedScreenAvailable() }
+ )
+ sendHingeAngleAndEnsureAnimationUpdate(90f, 120f, 180f)
+ runOnProgressThreadWithInterval(
+ { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
)
with(listener.ensureTransitionFinished()) {
@@ -91,7 +103,7 @@
}
@Test
- fun testUnfold_screenAvailableOnlyAfterFullUnfold_emitsIncreasingTransitionEvents() {
+ fun testUnfold_screenAvailableOnlyAfterFullUnfold_finishesWithUnfoldEvent() {
runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
{ foldStateProvider.sendHingeAngleUpdate(10f) },
@@ -102,7 +114,6 @@
)
with(listener.ensureTransitionFinished()) {
- assertIncreasingProgress()
assertFinishedWithUnfold()
}
}
@@ -111,9 +122,9 @@
fun testFold_emitsDecreasingTransitionEvents() {
runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_CLOSING) },
- { foldStateProvider.sendHingeAngleUpdate(170f) },
- { foldStateProvider.sendHingeAngleUpdate(90f) },
- { foldStateProvider.sendHingeAngleUpdate(10f) },
+ )
+ sendHingeAngleAndEnsureAnimationUpdate(170f, 90f, 10f)
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) },
)
@@ -127,9 +138,9 @@
fun testUnfoldAndStopUnfolding_finishesTheUnfoldTransition() {
runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) },
- { foldStateProvider.sendUnfoldedScreenAvailable() },
- { foldStateProvider.sendHingeAngleUpdate(10f) },
- { foldStateProvider.sendHingeAngleUpdate(90f) },
+ { foldStateProvider.sendUnfoldedScreenAvailable() })
+ sendHingeAngleAndEnsureAnimationUpdate(10f, 50f, 90f)
+ runOnProgressThreadWithInterval(
{ foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) },
)
@@ -159,12 +170,22 @@
with(listener.ensureTransitionFinished()) { assertHasFoldAnimationAtTheEnd() }
}
+ private fun sendHingeAngleAndEnsureAnimationUpdate(vararg angles: Float) {
+ angles.forEach { angle ->
+ listener.waitForProgressChangeAfter {
+ bgHandler.post {
+ foldStateProvider.sendHingeAngleUpdate(angle)
+ }
+ }
+ }
+ }
+
private fun runOnProgressThreadWithInterval(
vararg blocks: () -> Unit,
intervalMillis: Long = 60,
) {
blocks.forEach {
- fakeHandler.post(it)
+ bgHandler.post(it)
Thread.sleep(intervalMillis)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
index 4989a21..a7b67e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.unfold.progress
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
@@ -24,7 +24,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class RemoteUnfoldTransitionReceiverTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
index bbc96f70..6e8bf85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
@@ -68,6 +68,24 @@
return recordings.first()
}
+ /**
+ * Number of progress event for the currently running transition
+ * Returns null if there is no currently running transition
+ */
+ val currentTransitionProgressEventCount: Int?
+ get() = currentRecording?.progressHistory?.size
+
+ /**
+ * Runs [block] and ensures that there was at least once onTransitionProgress event after that
+ */
+ fun waitForProgressChangeAfter(block: () -> Unit) {
+ val eventCount = currentTransitionProgressEventCount
+ block()
+ waitForCondition {
+ currentTransitionProgressEventCount != eventCount
+ }
+ }
+
fun assertStarted() {
assertWithMessage("Transition didn't start").that(currentRecording).isNotNull()
}
@@ -86,7 +104,7 @@
}
class UnfoldTransitionRecording {
- private val progressHistory: MutableList<Float> = arrayListOf()
+ val progressHistory: MutableList<Float> = arrayListOf()
private var finishingInvocations: Int = 0
fun addProgress(progress: Float) {
@@ -142,6 +160,6 @@
}
private companion object {
- private const val MIN_ANIMATION_EVENTS = 5
+ private const val MIN_ANIMATION_EVENTS = 3
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
index 70eadce..b93c161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.unfold.progress
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.SysuiTestCase
@@ -25,7 +25,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class UnfoldRemoteFilterTest : SysuiTestCase() {
private val listener = TestUnfoldProgressListener()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 552b60c..21a45ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -21,8 +21,8 @@
import android.content.res.Resources
import android.os.Handler
import android.os.Looper
-import android.testing.AndroidTestingRunner
import androidx.core.util.Consumer
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
@@ -50,7 +50,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DeviceFoldStateProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
index 4eb1591..4d9c80f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.unfold.updates
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -32,7 +32,7 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DeviceStateRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
index eaef007..f5bcc21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
@@ -21,9 +21,9 @@
import android.os.HandlerThread
import android.os.Looper
import android.os.Process
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
@@ -41,7 +41,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
class RotationChangeProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index 70ec050..8d892ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.unfold.util
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.FakeUnfoldTransitionProvider
@@ -36,7 +36,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index 451bd24..39977c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -18,8 +18,8 @@
import android.content.ContentResolver
import android.database.ContentObserver
import android.provider.Settings
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.FakeUnfoldTransitionProvider
@@ -35,7 +35,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
index 4486402..6e0bff2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
@@ -19,8 +19,8 @@
import android.os.Handler
import android.os.HandlerThread
import android.os.Process
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.FakeUnfoldTransitionProvider
@@ -38,7 +38,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
class ScopedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
index cd4d7b5..0cbe1ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.unfold.util
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.FakeUnfoldTransitionProvider
@@ -26,7 +26,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
index b04eb01..32c5986 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
@@ -20,9 +20,9 @@
import android.hardware.usb.IUsbSerialReader
import android.hardware.usb.UsbAccessory
import android.hardware.usb.UsbManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import com.android.systemui.SysuiTestCase
@@ -40,7 +40,7 @@
/**
* UsbPermissionActivityTest
*/
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class UsbPermissionActivityTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
index 6db35ae..84cd79d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
@@ -1,9 +1,9 @@
package com.android.systemui.user
import android.app.Dialog
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -15,7 +15,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class CreateUserActivityTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
index 35f9c41..a291140 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
@@ -3,8 +3,8 @@
import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -22,7 +22,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class UserCreatorTest : SysuiTestCase() {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 0669cb8..37a73cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -21,6 +21,7 @@
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
@@ -41,7 +42,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when` as whenever
@@ -49,7 +49,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class UserRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var manager: UserManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index 01795e9..20b273a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -22,6 +22,7 @@
import android.content.pm.UserInfo
import android.os.UserHandle
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.GuestResetOrExitSessionReceiver
@@ -39,7 +40,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.never
@@ -47,7 +47,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class GuestUserInteractorTest : SysuiTestCase() {
@Mock private lateinit var manager: UserManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
index b30f77a..59ad38a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.user.domain.interactor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -26,11 +27,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class RefreshUsersSchedulerTest : SysuiTestCase() {
private lateinit var underTest: RefreshUsersScheduler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
index 140e919..78028f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
@@ -1,6 +1,7 @@
package com.android.systemui.user.domain.interactor
import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER
import com.android.systemui.SysuiTestCase
@@ -10,10 +11,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class SelectedUserInteractorTest : SysuiTestCase() {
private lateinit var underTest: SelectedUserInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index 96c6eb8..ccbdffa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -28,6 +28,7 @@
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
@@ -75,7 +76,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
@@ -89,7 +89,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class UserSwitcherInteractorTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 661837b..99cdf73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -23,6 +23,7 @@
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
@@ -60,7 +61,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.doAnswer
@@ -68,7 +68,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class StatusBarUserChipViewModelTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var activityManager: ActivityManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 5661e20..917df61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -21,6 +21,7 @@
import android.app.admin.DevicePolicyManager
import android.content.pm.UserInfo
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
@@ -62,13 +63,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class UserSwitcherViewModelTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
index 8bfff9c2..b3f2113 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import android.content.pm.ComponentInfo
import android.content.pm.PackageManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
index df08efa..c896fc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
@@ -23,8 +23,8 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.SensorEventListener;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class AsyncSensorManagerTest extends SysuiTestCase {
private AsyncSensorManager mAsyncSensorManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index eb11e38..ef8d51a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -22,8 +22,8 @@
import android.os.Handler
import android.os.Looper
import android.provider.Settings.SettingNotFoundException
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -36,7 +36,7 @@
import org.mockito.kotlin.eq
/** Tests for [SettingsProxy]. */
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class SettingsProxyTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index 38469ee..c08ca7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -23,8 +23,8 @@
import android.os.Handler
import android.os.Looper
import android.provider.Settings
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
@@ -39,7 +39,7 @@
import org.mockito.kotlin.eq
/** Tests for [UserSettingsProxy]. */
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class UserSettingsProxyTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index 7d89a55..bdecf2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -20,6 +20,7 @@
import android.app.WallpaperManager
import android.content.Intent
import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -37,9 +38,11 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
class WallpaperRepositoryImplTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fabb9b7..c5fbc39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -25,6 +25,8 @@
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static androidx.test.ext.truth.content.IntentSubject.assertThat;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR;
@@ -2017,6 +2019,31 @@
}
@Test
+ public void testShowOrHideAppBubble_updateExistedBubbleInOverflow_updateIntentInBubble() {
+ String appBubbleKey = Bubble.getAppBubbleKeyForApp(mAppBubbleIntent.getPackage(), mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
+ // Collapse the stack so we don't need to wait for the dismiss animation in the test
+ mBubbleController.collapseStack();
+ // Dismiss the app bubble so it's in the overflow
+ mBubbleController.dismissBubble(appBubbleKey, Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNotNull();
+
+ // Modify the intent to include new extras.
+ Intent newAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class)
+ .setPackage(mContext.getPackageName())
+ .putExtra("hello", "world");
+
+ // Calling this while collapsed will re-add and expand the app bubble
+ mBubbleController.showOrHideAppBubble(newAppBubbleIntent, mUser0, mAppBubbleIcon);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
+ assertThat(mBubbleController.isStackExpanded()).isTrue();
+ assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
+ assertThat(mBubbleData.getBubbles().get(0).getAppBubbleIntent()).extras().string(
+ "hello").isEqualTo("world");
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull();
+ }
+
+ @Test
public void testCreateBubbleFromOngoingNotification() {
NotificationEntry notif = new NotificationEntryBuilder()
.setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/android/hardware/display/AmbientDisplayConfigurationKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/hardware/display/AmbientDisplayConfigurationKosmos.kt
index d8af3fa..3f3c30f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/hardware/display/AmbientDisplayConfigurationKosmos.kt
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package android.hardware.display
+import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.ambientDisplayConfiguration by Fixture {
+ FakeAmbientDisplayConfiguration(applicationContext)
+}
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
new file mode 100644
index 0000000..36ac4a4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input
+
+import android.view.InputDevice
+import android.view.KeyCharacterMap
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.invocation.InvocationOnMock
+
+class FakeInputManager {
+
+ private val devices = mutableMapOf<Int, InputDevice>()
+
+ val inputManager =
+ mock<InputManager> {
+ whenever(getInputDevice(anyInt())).thenAnswer { invocation ->
+ val deviceId = invocation.arguments[0] as Int
+ return@thenAnswer devices[deviceId]
+ }
+ whenever(inputDeviceIds).thenAnswer {
+ return@thenAnswer devices.keys.toIntArray()
+ }
+
+ fun setDeviceEnabled(invocation: InvocationOnMock, enabled: Boolean) {
+ val deviceId = invocation.arguments[0] as Int
+ val device = devices[deviceId] ?: return
+ devices[deviceId] = device.copy(enabled = enabled)
+ }
+
+ whenever(disableInputDevice(anyInt())).thenAnswer { invocation ->
+ setDeviceEnabled(invocation, enabled = false)
+ }
+ whenever(enableInputDevice(anyInt())).thenAnswer { invocation ->
+ setDeviceEnabled(invocation, enabled = true)
+ }
+ }
+
+ fun addPhysicalKeyboard(id: Int, enabled: Boolean = true) {
+ check(id > 0) { "Physical keyboard ids have to be > 0" }
+ addKeyboard(id, enabled)
+ }
+
+ fun addVirtualKeyboard(enabled: Boolean = true) {
+ addKeyboard(id = KeyCharacterMap.VIRTUAL_KEYBOARD, enabled)
+ }
+
+ private fun addKeyboard(id: Int, enabled: Boolean = true) {
+ devices[id] =
+ InputDevice.Builder()
+ .setId(id)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setEnabled(enabled)
+ .build()
+ }
+
+ private fun InputDevice.copy(
+ id: Int = getId(),
+ type: Int = keyboardType,
+ sources: Int = getSources(),
+ enabled: Boolean = isEnabled
+ ) =
+ InputDevice.Builder()
+ .setId(id)
+ .setKeyboardType(type)
+ .setSources(sources)
+ .setEnabled(enabled)
+ .build()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/InputManagerKosmos.kt
similarity index 81%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/android/hardware/input/InputManagerKosmos.kt
index d8af3fa..bf68eab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/InputManagerKosmos.kt
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package android.hardware.input
import com.android.systemui.kosmos.Kosmos
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.fakeInputManager by Kosmos.Fixture { FakeInputManager() }
diff --git a/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt
index 21dea6b..2ee289b 100644
--- a/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt
@@ -18,6 +18,8 @@
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
var Kosmos.layoutInflater: LayoutInflater by
Kosmos.Fixture { LayoutInflater.from(applicationContext) }
+var Kosmos.mockedLayoutInflater: LayoutInflater by Kosmos.Fixture { mock<LayoutInflater>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 8eef930..78d8abe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -21,7 +21,6 @@
import android.app.Instrumentation;
import android.content.Context;
import android.content.res.Resources;
-import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
@@ -297,7 +296,7 @@
}
public static boolean isRobolectricTest() {
- return !isRavenwoodTest() && Build.FINGERPRINT.contains("robolectric");
+ return !isRavenwoodTest() && !System.getProperty("java.vm.name").equals("Dalvik");
}
protected boolean isScreenshotTest() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index 9dae44d..5db9d31 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui
import android.app.ActivityManager
+import android.app.DreamManager
import android.app.admin.DevicePolicyManager
import android.app.trust.TrustManager
import android.os.UserManager
@@ -32,6 +33,7 @@
import com.android.systemui.biometrics.AuthController
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.ScreenLifecycle
@@ -93,6 +95,7 @@
@get:Provides val demoModeController: DemoModeController = mock(),
@get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),
@get:Provides val dozeParameters: DozeParameters = mock(),
+ @get:Provides val dreamManager: DreamManager = mock(),
@get:Provides val dumpManager: DumpManager = mock(),
@get:Provides val headsUpManager: HeadsUpManager = mock(),
@get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),
@@ -130,6 +133,7 @@
@get:Provides val systemUIDialogManager: SystemUIDialogManager = mock(),
@get:Provides val deviceEntryIconTransitions: Set<DeviceEntryIconTransition> = emptySet(),
@get:Provides val communalInteractor: CommunalInteractor = mock(),
+ @get:Provides val communalSceneInteractor: CommunalSceneInteractor = mock(),
@get:Provides val sceneLogger: SceneLogger = mock(),
@get:Provides val trustManager: TrustManager = mock(),
@get:Provides val primaryBouncerInteractor: PrimaryBouncerInteractor = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 4999a5a..43b57de 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -26,6 +26,8 @@
import com.android.systemui.biometrics.udfpsUtils
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.util.mockito.mock
+import org.mockito.Mockito.mock
val Kosmos.promptViewModel by Fixture {
PromptViewModel(
@@ -35,7 +37,9 @@
udfpsOverlayInteractor = udfpsOverlayInteractor,
biometricStatusInteractor = biometricStatusInteractor,
udfpsUtils = udfpsUtils,
- iconProvider = IconProvider(applicationContext),
+ iconProvider = iconProvider,
activityTaskManager = activityTaskManager,
)
}
+
+val Kosmos.iconProvider by Fixture { mock<IconProvider>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index f75cdd4..9236bd2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -28,6 +29,7 @@
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.systemClock
val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by
@@ -47,3 +49,24 @@
sceneInteractor = { sceneInteractor },
)
}
+
+fun Kosmos.givenCanShowAlternateBouncer() {
+ this.givenAlternateBouncerSupported()
+ this.keyguardBouncerRepository.setPrimaryShow(false)
+ this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+ whenever(this.keyguardStateController.isUnlocked).thenReturn(false)
+}
+
+fun Kosmos.givenAlternateBouncerSupported() {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ this.fingerprintPropertyRepository.supportsUdfps()
+ } else {
+ this.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true)
+ }
+}
+
+fun Kosmos.givenCannotShowAlternateBouncer() {
+ this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
index d3ed58b..1da1fb2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
@@ -17,16 +17,28 @@
package com.android.systemui.communal.data.repository
+import android.content.pm.UserInfo
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
/** Fake implementation of [CommunalPrefsRepository] */
class FakeCommunalPrefsRepository : CommunalPrefsRepository {
- private val _isCtaDismissed = MutableStateFlow(false)
- override val isCtaDismissed: Flow<Boolean> = _isCtaDismissed.asStateFlow()
+ private val _isCtaDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+ private val _isDisclaimerDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
- override suspend fun setCtaDismissedForCurrentUser() {
- _isCtaDismissed.value = true
+ override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
+ _isCtaDismissed.map { it.contains(user) }
+
+ override fun isDisclaimerDismissed(user: UserInfo): Flow<Boolean> =
+ _isDisclaimerDismissed.map { it.contains(user) }
+
+ override suspend fun setCtaDismissed(user: UserInfo) {
+ _isCtaDismissed.value = _isCtaDismissed.value.toMutableSet().apply { add(user) }
+ }
+
+ override suspend fun setDisclaimerDismissed(user: UserInfo) {
+ _isDisclaimerDismissed.value =
+ _isDisclaimerDismissed.value.toMutableSet().apply { add(user) }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 1583d1c..b58861b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -19,7 +19,6 @@
import android.os.userManager
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.repository.communalMediaRepository
-import com.android.systemui.communal.data.repository.communalPrefsRepository
import com.android.systemui.communal.data.repository.communalWidgetRepository
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.flags.Flags
@@ -46,7 +45,7 @@
broadcastDispatcher = broadcastDispatcher,
communalSceneInteractor = communalSceneInteractor,
widgetRepository = communalWidgetRepository,
- communalPrefsRepository = communalPrefsRepository,
+ communalPrefsInteractor = communalPrefsInteractor,
mediaRepository = communalMediaRepository,
smartspaceRepository = smartspaceRepository,
keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorKosmos.kt
new file mode 100644
index 0000000..37563c4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.communalPrefsRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.communalPrefsInteractor: CommunalPrefsInteractor by
+ Kosmos.Fixture {
+ CommunalPrefsInteractor(
+ applicationCoroutineScope,
+ communalPrefsRepository,
+ selectedUserInteractor,
+ userTracker,
+ tableLogBuffer = mock()
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryKosmos.kt
new file mode 100644
index 0000000..e4efadbd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.deviceconfig.data.repository
+
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.util.deviceConfigProxy
+
+val Kosmos.deviceConfigRepository by Fixture {
+ DeviceConfigRepository(
+ backgroundExecutor = fakeExecutor,
+ backgroundDispatcher = testDispatcher,
+ dataSource = deviceConfigProxy,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorKosmos.kt
index d8af3fa..d538497 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorKosmos.kt
@@ -14,8 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.deviceconfig.domain.interactor
+import com.android.systemui.deviceconfig.data.repository.deviceConfigRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.deviceConfigInteractor by Fixture {
+ DeviceConfigInteractor(
+ repository = deviceConfigRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 3401cc4..f51036f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -17,10 +17,16 @@
package com.android.systemui.keyboard.shortcut
import android.content.applicationContext
+import android.content.res.mainResources
+import android.hardware.input.fakeInputManager
import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperTestHelper
-import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperInteractor
+import com.android.systemui.keyboard.shortcut.data.source.MultitaskingShortcutsSource
+import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
import com.android.systemui.keyboard.shortcut.ui.ShortcutHelperActivityStarter
import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
import com.android.systemui.keyguard.data.repository.fakeCommandQueue
@@ -31,26 +37,61 @@
import com.android.systemui.model.sysUiState
import com.android.systemui.settings.displayTracker
-val Kosmos.shortcutHelperRepository by
- Kosmos.Fixture { ShortcutHelperRepository(fakeCommandQueue, broadcastDispatcher) }
+val Kosmos.shortcutHelperSystemShortcutsSource by
+ Kosmos.Fixture { SystemShortcutsSource(mainResources) }
+
+val Kosmos.shortcutHelperMultiTaskingShortcutsSource by
+ Kosmos.Fixture { MultitaskingShortcutsSource(mainResources) }
+
+val Kosmos.shortcutHelperStateRepository by
+ Kosmos.Fixture {
+ ShortcutHelperStateRepository(
+ fakeCommandQueue,
+ broadcastDispatcher,
+ fakeInputManager.inputManager,
+ testScope,
+ testDispatcher
+ )
+ }
+
+val Kosmos.shortcutHelperCategoriesRepository by
+ Kosmos.Fixture {
+ ShortcutHelperCategoriesRepository(
+ shortcutHelperSystemShortcutsSource,
+ shortcutHelperMultiTaskingShortcutsSource,
+ )
+ }
val Kosmos.shortcutHelperTestHelper by
Kosmos.Fixture {
ShortcutHelperTestHelper(
- shortcutHelperRepository,
+ shortcutHelperStateRepository,
applicationContext,
broadcastDispatcher,
fakeCommandQueue
)
}
-val Kosmos.shortcutHelperInteractor by
+val Kosmos.shortcutHelperStateInteractor by
Kosmos.Fixture {
- ShortcutHelperInteractor(displayTracker, testScope, sysUiState, shortcutHelperRepository)
+ ShortcutHelperStateInteractor(
+ displayTracker,
+ testScope,
+ sysUiState,
+ shortcutHelperStateRepository
+ )
+ }
+
+val Kosmos.shortcutHelperCategoriesInteractor by
+ Kosmos.Fixture {
+ ShortcutHelperCategoriesInteractor(
+ shortcutHelperStateRepository,
+ shortcutHelperCategoriesRepository
+ )
}
val Kosmos.shortcutHelperViewModel by
- Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperInteractor) }
+ Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperStateInteractor) }
val Kosmos.fakeShortcutHelperStartActivity by Kosmos.Fixture { FakeShortcutHelperStartActivity() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
index e6e7b87..36608ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
@@ -22,7 +22,7 @@
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
class ShortcutHelperTestHelper(
- repo: ShortcutHelperRepository,
+ repo: ShortcutHelperStateRepository,
private val context: Context,
private val fakeBroadcastDispatcher: FakeBroadcastDispatcher,
private val fakeCommandQueue: FakeCommandQueue,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/DismissCallbackRegistryKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/DismissCallbackRegistryKosmos.kt
index d8af3fa..b12f947 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/DismissCallbackRegistryKosmos.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.keyguard
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.dismissCallbackRegistry by Fixture { DismissCallbackRegistry(fakeExecutor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index dca531a..5bae6ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -133,6 +133,8 @@
override val topClippingBounds = MutableStateFlow<Int?>(null)
+ private var isShowKeyguardWhenReenabled: Boolean = false
+
override fun setQuickSettingsVisible(isVisible: Boolean) {
_isQuickSettingsVisible.value = isVisible
}
@@ -268,6 +270,14 @@
fun setIsEncryptedOrLockdown(value: Boolean) {
_isEncryptedOrLockdown.value = value
}
+
+ override fun setShowKeyguardWhenReenabled(isShowKeyguardWhenReenabled: Boolean) {
+ this.isShowKeyguardWhenReenabled = isShowKeyguardWhenReenabled
+ }
+
+ override fun isShowKeyguardWhenReenabled(): Boolean {
+ return isShowKeyguardWhenReenabled
+ }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
index cd83c2f..39a1a63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
@@ -33,9 +33,10 @@
private val _isTrustUsuallyManaged = MutableStateFlow(false)
override val isCurrentUserTrustUsuallyManaged: StateFlow<Boolean>
get() = _isTrustUsuallyManaged
+
private val _isCurrentUserTrusted = MutableStateFlow(false)
- override val isCurrentUserTrusted: Flow<Boolean>
- get() = _isCurrentUserTrusted
+ override val isCurrentUserTrusted: StateFlow<Boolean>
+ get() = _isCurrentUserTrusted.asStateFlow()
private val _isCurrentUserActiveUnlockAvailable = MutableStateFlow(false)
override val isCurrentUserActiveUnlockRunning: StateFlow<Boolean> =
@@ -48,6 +49,13 @@
private val _requestDismissKeyguard = MutableStateFlow(TrustModel(false, 0, TrustGrantFlags(0)))
override val trustAgentRequestingToDismissKeyguard: Flow<TrustModel> = _requestDismissKeyguard
+ var keyguardShowingChangeEventCount: Int = 0
+ private set
+
+ override suspend fun reportKeyguardShowingChanged() {
+ keyguardShowingChangeEventCount++
+ }
+
fun setCurrentUserTrusted(trust: Boolean) {
_isCurrentUserTrusted.value = trust
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
index edf77a0..744b127 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
@@ -16,7 +16,9 @@
package com.android.systemui.keyguard.domain.interactor
+import android.service.dream.dreamManager
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
@@ -35,8 +37,10 @@
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
communalInteractor = communalInteractor,
+ communalSceneInteractor = communalSceneInteractor,
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
deviceEntryRepository = deviceEntryRepository,
+ dreamManager = dreamManager
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
index c06f833..73799b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
@@ -24,10 +24,11 @@
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.pulsingGestureListener
-val Kosmos.keyguardLongPressInteractor by
+val Kosmos.keyguardTouchHandlingInteractor by
Kosmos.Fixture {
- KeyguardLongPressInteractor(
+ KeyguardTouchHandlingInteractor(
appContext = applicationContext,
scope = applicationCoroutineScope,
transitionInteractor = keyguardTransitionInteractor,
@@ -36,5 +37,6 @@
featureFlags = featureFlagsClassic,
broadcastDispatcher = broadcastDispatcher,
accessibilityManager = accessibilityManagerWrapper,
+ pulsingGestureListener = pulsingGestureListener,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/TrustInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/TrustInteractorKosmos.kt
index 0ebf164..d60326c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/TrustInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/TrustInteractorKosmos.kt
@@ -19,5 +19,11 @@
import com.android.systemui.keyguard.data.repository.trustRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
-val Kosmos.trustInteractor by Fixture { TrustInteractor(repository = trustRepository) }
+val Kosmos.trustInteractor by Fixture {
+ TrustInteractor(
+ applicationScope = applicationCoroutineScope,
+ repository = trustRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
new file mode 100644
index 0000000..6eb8a49
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.keyguard.ui.binder
+
+import android.content.applicationContext
+import android.view.layoutInflater
+import android.view.mockedLayoutInflater
+import android.view.windowManager
+import com.android.systemui.biometrics.domain.interactor.fingerprintPropertyInteractor
+import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerWindowViewModel
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@OptIn(ExperimentalCoroutinesApi::class)
+val Kosmos.alternateBouncerViewBinder by
+ Kosmos.Fixture {
+ AlternateBouncerViewBinder(
+ applicationScope = applicationCoroutineScope,
+ alternateBouncerWindowViewModel = { alternateBouncerWindowViewModel },
+ alternateBouncerDependencies = { alternateBouncerDependencies },
+ windowManager = { windowManager },
+ layoutInflater = { mockedLayoutInflater },
+ )
+ }
+
+private val Kosmos.alternateBouncerDependencies by
+ Kosmos.Fixture {
+ AlternateBouncerDependencies(
+ viewModel = mock<AlternateBouncerViewModel>(),
+ swipeUpAnywhereGestureHandler = mock<SwipeUpAnywhereGestureHandler>(),
+ tapGestureDetector = mock<TapGestureDetector>(),
+ udfpsIconViewModel = alternateBouncerUdfpsIconViewModel,
+ udfpsAccessibilityOverlayViewModel = {
+ mock<AlternateBouncerUdfpsAccessibilityOverlayViewModel>()
+ },
+ messageAreaViewModel = mock<AlternateBouncerMessageAreaViewModel>(),
+ powerInteractor = powerInteractor,
+ )
+ }
+
+private val Kosmos.alternateBouncerUdfpsIconViewModel by
+ Kosmos.Fixture {
+ AlternateBouncerUdfpsIconViewModel(
+ context = applicationContext,
+ configurationInteractor = configurationInteractor,
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ deviceEntryBackgroundViewModel = mock<DeviceEntryBackgroundViewModel>(),
+ fingerprintPropertyInteractor = fingerprintPropertyInteractor,
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ alternateBouncerViewModel = alternateBouncerViewModel,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
index 6f168d4..6cf668c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
@@ -33,6 +33,7 @@
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+ lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
keyguardClockViewModel = keyguardClockViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
index 19e4241..8549a30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
var Kosmos.goneToAodTransitionViewModel by Fixture {
GoneToAodTransitionViewModel(
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ powerInteractor = powerInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModelKosmos.kt
index 3c9846a..281d7b0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardLongPressViewModelKosmos.kt
@@ -16,12 +16,12 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.keyguard.domain.interactor.keyguardLongPressInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTouchHandlingInteractor
import com.android.systemui.kosmos.Kosmos
-val Kosmos.keyguardLongPressViewModel by
+val Kosmos.keyguardTouchHandlingViewModel by
Kosmos.Fixture {
- KeyguardLongPressViewModel(
- interactor = keyguardLongPressInteractor,
+ KeyguardTouchHandlingViewModel(
+ interactor = keyguardTouchHandlingInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 30a4f21..24e47b0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -30,7 +30,7 @@
clockInteractor = keyguardClockInteractor,
interactor = keyguardBlueprintInteractor,
authController = authController,
- longPress = keyguardLongPressViewModel,
+ touchHandling = keyguardTouchHandlingViewModel,
shadeInteractor = shadeInteractor,
applicationScope = applicationCoroutineScope,
unfoldTransitionInteractor = unfoldTransitionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
index 07b4cd4..f45e33b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
-val Kosmos.lockscreenToAodTransitionViewModel by Fixture {
+var Kosmos.lockscreenToAodTransitionViewModel by Fixture {
LockscreenToAodTransitionViewModel(
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ powerInteractor = powerInteractor,
shadeDependentFlows = shadeDependentFlows,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 31cdbc7..10ff731 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -50,6 +50,7 @@
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.scrimStartable
import com.android.systemui.scene.sceneContainerConfig
@@ -142,4 +143,5 @@
val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
val scrimController by lazy { kosmos.scrimController }
val scrimStartable by lazy { kosmos.scrimStartable }
+ val sceneContainerOcclusionInteractor by lazy { kosmos.sceneContainerOcclusionInteractor }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
index 690bde7..7a04aa2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryKosmos.kt
@@ -18,6 +18,7 @@
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.util.time.systemClock
@@ -26,6 +27,7 @@
MediaFilterRepository(
applicationContext = applicationContext,
systemClock = systemClock,
- configurationController = configurationController
+ configurationController = configurationController,
+ smartspaceLogger = mediaSmartspaceLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
index 81adefa..6e650a3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.controls.domain.pipeline.interactor
-import android.content.applicationContext
import com.android.systemui.activityIntentHelper
import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.kosmos.Kosmos
@@ -31,7 +30,6 @@
val Kosmos.mediaControlInteractor by
Kosmos.Fixture {
MediaControlInteractor(
- applicationContext = applicationContext,
instanceId = mediaInstanceId,
repository = mediaFilterRepository,
mediaDataProcessor = mediaDataProcessor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
index 461eaa2..e490b75 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.controls.domain.pipeline.interactor.factory
-import android.content.applicationContext
import com.android.internal.logging.InstanceId
import com.android.systemui.activityIntentHelper
import com.android.systemui.bluetooth.mockBroadcastDialogController
@@ -34,7 +33,6 @@
object : MediaControlInteractorFactory {
override fun create(instanceId: InstanceId): MediaControlInteractor {
return MediaControlInteractor(
- applicationContext = applicationContext,
instanceId = instanceId,
repository = mediaFilterRepository,
mediaDataProcessor = mediaDataProcessor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaSmartspaceLoggerKosmos.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaSmartspaceLoggerKosmos.kt
index d8af3fa..c63dec5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaSmartspaceLoggerKosmos.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.media.controls.util
import com.android.systemui.kosmos.Kosmos
+import org.mockito.Mockito.mock
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+var Kosmos.mediaSmartspaceLogger by Kosmos.Fixture { MediaSmartspaceLogger() }
+val Kosmos.mockMediaSmartspaceLogger by Kosmos.Fixture { mock(MediaSmartspaceLogger::class.java) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/data/repository/NavigationRepositoryKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/navigation/data/repository/NavigationRepositoryKosmos.kt
index d8af3fa..717a300 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/data/repository/NavigationRepositoryKosmos.kt
@@ -14,8 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.navigation.data.repository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.navigationbar.navigationModeController
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.navigationRepository by Fixture {
+ NavigationRepository(
+ controller = navigationModeController,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorKosmos.kt
index d8af3fa..80fe3b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorKosmos.kt
@@ -14,8 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.navigation.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.navigation.data.repository.navigationRepository
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.navigationInteractor by Fixture {
+ NavigationInteractor(
+ repository = navigationRepository,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationModeControllerKosmos.kt
similarity index 74%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationModeControllerKosmos.kt
index d8af3fa..f2fb1a8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationModeControllerKosmos.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.navigationbar
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.util.mockito.mock
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.navigationModeController by Fixture { mock<NavigationModeController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index ec56327..f9f8d23 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.StatusBarStateControllerImpl
@@ -38,6 +39,7 @@
{ shadeInteractor },
{ deviceUnlockedInteractor },
{ sceneInteractor },
+ { sceneContainerOcclusionInteractor },
{ keyguardClockInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
similarity index 88%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
index d8af3fa..2f5daaa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/FixedColumnsRepositoryKosmos.kt
@@ -18,4 +18,4 @@
import com.android.systemui.kosmos.Kosmos
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.fixedColumnsRepository by Kosmos.Fixture { FixedColumnsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryKosmos.kt
index d8af3fa..696c4bf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryKosmos.kt
@@ -16,6 +16,12 @@
package com.android.systemui.qs.panels.data.repository
+import com.android.systemui.common.ui.data.repository.configurationRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.paginatedGridRepository by
+ Kosmos.Fixture {
+ testCase.context.orCreateTestableResources
+ PaginatedGridRepository(testCase.context.resources, configurationRepository)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
similarity index 78%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
index 6e11977..f4d281d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FixedColumnsSizeInteractorKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.infiniteGridSizeRepository
+import com.android.systemui.qs.panels.data.repository.fixedColumnsRepository
-val Kosmos.infiniteGridSizeInteractor by
- Kosmos.Fixture { InfiniteGridSizeInteractor(infiniteGridSizeRepository) }
+val Kosmos.fixedColumnsSizeInteractor by
+ Kosmos.Fixture { FixedColumnsSizeInteractor(fixedColumnsRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorKosmos.kt
index 7f387d7..320c2ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorKosmos.kt
@@ -20,5 +20,5 @@
val Kosmos.infiniteGridConsistencyInteractor by
Kosmos.Fixture {
- InfiniteGridConsistencyInteractor(iconTilesInteractor, infiniteGridSizeInteractor)
+ InfiniteGridConsistencyInteractor(iconTilesInteractor, fixedColumnsSizeInteractor)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
index 82cfaf5..be00152 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
@@ -18,8 +18,8 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
+import com.android.systemui.qs.panels.ui.viewmodel.fixedColumnsSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridSizeViewModel
val Kosmos.infiniteGridLayout by
- Kosmos.Fixture { InfiniteGridLayout(iconTilesViewModel, infiniteGridSizeViewModel) }
+ Kosmos.Fixture { InfiniteGridLayout(iconTilesViewModel, fixedColumnsSizeViewModel) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PaginatedGridInteractorKosmos.kt
similarity index 78%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PaginatedGridInteractorKosmos.kt
index 6e11977..a922e5d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/PaginatedGridInteractorKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.infiniteGridSizeRepository
+import com.android.systemui.qs.panels.data.repository.paginatedGridRepository
-val Kosmos.infiniteGridSizeInteractor by
- Kosmos.Fixture { InfiniteGridSizeInteractor(infiniteGridSizeRepository) }
+val Kosmos.paginatedGridInteractor by
+ Kosmos.Fixture { PaginatedGridInteractor(paginatedGridRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModelKosmos.kt
similarity index 79%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModelKosmos.kt
index f6dfb8b..feadc91 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/FixedColumnsSizeViewModelKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridSizeInteractor
+import com.android.systemui.qs.panels.domain.interactor.fixedColumnsSizeInteractor
-val Kosmos.infiniteGridSizeViewModel by
- Kosmos.Fixture { InfiniteGridSizeViewModelImpl(infiniteGridSizeInteractor) }
+val Kosmos.fixedColumnsSizeViewModel by
+ Kosmos.Fixture { FixedColumnsSizeViewModelImpl(fixedColumnsSizeInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MockTileViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MockTileViewModel.kt
new file mode 100644
index 0000000..5386ece
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MockTileViewModel.kt
@@ -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.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+fun MockTileViewModel(
+ spec: TileSpec,
+ state: StateFlow<QSTile.State> = MutableStateFlow(QSTile.State())
+): TileViewModel = mock {
+ whenever(this.spec).thenReturn(spec)
+ whenever(this.state).thenReturn(state)
+ whenever(this.currentState).thenReturn(state.value)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index f6dfb8b..85e9265 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridSizeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -17,7 +17,16 @@
package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridSizeInteractor
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor
-val Kosmos.infiniteGridSizeViewModel by
- Kosmos.Fixture { InfiniteGridSizeViewModelImpl(infiniteGridSizeInteractor) }
+val Kosmos.paginatedGridViewModel by
+ Kosmos.Fixture {
+ PaginatedGridViewModel(
+ iconTilesViewModel,
+ fixedColumnsSizeViewModel,
+ iconLabelVisibilityViewModel,
+ paginatedGridInteractor,
+ applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
index b07cc7d..fde174d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PartitionedGridViewModelKosmos.kt
@@ -22,7 +22,7 @@
Kosmos.Fixture {
PartitionedGridViewModel(
iconTilesViewModel,
- infiniteGridSizeViewModel,
+ fixedColumnsSizeViewModel,
iconLabelVisibilityViewModel,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index 066736c..0921eb9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.interactor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
@@ -31,5 +32,6 @@
logger = sceneLogger,
sceneFamilyResolvers = { sceneFamilyResolvers },
deviceUnlockedInteractor = deviceUnlockedInteractor,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
index 6be1939..ae33aea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
@@ -20,6 +20,7 @@
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.shared.model.SceneFamilies
@@ -39,6 +40,7 @@
HomeSceneFamilyResolver(
applicationScope = applicationCoroutineScope,
deviceEntryInteractor = deviceEntryInteractor,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartableKosmos.kt
new file mode 100644
index 0000000..f9111bf
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartableKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.scene.domain.startable
+
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.trustInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+
+val Kosmos.keyguardStateCallbackStartable by Fixture {
+ KeyguardStateCallbackStartable(
+ applicationScope = applicationCoroutineScope,
+ backgroundDispatcher = testDispatcher,
+ sceneInteractor = sceneInteractor,
+ selectedUserInteractor = selectedUserInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
+ simBouncerInteractor = simBouncerInteractor,
+ trustInteractor = trustInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
similarity index 84%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index cf18c0e..03a42bc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.scene.domain.interactor
+package com.android.systemui.scene.domain.startable
import com.android.internal.logging.uiEventLogger
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
@@ -25,14 +25,19 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.keyguard.dismissCallbackRegistry
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.scene.domain.startable.SceneContainerStartable
+import com.android.systemui.scene.domain.interactor.sceneBackInteractor
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.session.shared.shadeSessionStorage
import com.android.systemui.scene.shared.logger.sceneLogger
import com.android.systemui.settings.displayTracker
@@ -50,6 +55,7 @@
deviceUnlockedInteractor = deviceUnlockedInteractor,
bouncerInteractor = bouncerInteractor,
keyguardInteractor = keyguardInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
sysUiState = sysUiState,
displayId = displayTracker.defaultDisplayId,
sceneLogger = sceneLogger,
@@ -69,5 +75,7 @@
sceneBackInteractor = sceneBackInteractor,
shadeSessionStorage = shadeSessionStorage,
windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
+ dismissCallbackRegistry = dismissCallbackRegistry,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/StatusBarStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/StatusBarStartableKosmos.kt
new file mode 100644
index 0000000..ee69c30
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/StatusBarStartableKosmos.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.scene.domain.startable
+
+import android.content.applicationContext
+import com.android.internal.statusbar.statusBarService
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.deviceconfig.domain.interactor.deviceConfigInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.navigation.domain.interactor.navigationInteractor
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.statusBarStartable by Fixture {
+ StatusBarStartable(
+ applicationScope = applicationCoroutineScope,
+ backgroundDispatcher = testDispatcher,
+ applicationContext = applicationContext,
+ selectedUserInteractor = selectedUserInteractor,
+ sceneInteractor = sceneInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
+ sceneContainerOcclusionInteractor = sceneContainerOcclusionInteractor,
+ deviceConfigInteractor = deviceConfigInteractor,
+ navigationInteractor = navigationInteractor,
+ authenticationInteractor = authenticationInteractor,
+ powerInteractor = powerInteractor,
+ deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
+ statusBarService = statusBarService,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/PulsingGestureListenerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/PulsingGestureListenerKosmos.kt
new file mode 100644
index 0000000..4fc2228
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/PulsingGestureListenerKosmos.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.shade
+
+import android.hardware.display.ambientDisplayConfiguration
+import com.android.systemui.classifier.falsingManager
+import com.android.systemui.dock.dockManager
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.domain.interactor.dozeInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.settings.userTracker
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.pulsingGestureListener by Fixture {
+ PulsingGestureListener(
+ falsingManager = falsingManager,
+ dockManager = dockManager,
+ powerInteractor = powerInteractor,
+ ambientDisplayConfiguration = ambientDisplayConfiguration,
+ statusBarStateController = statusBarStateController,
+ shadeLogger = mock(),
+ dozeInteractor = dozeInteractor,
+ userTracker = userTracker,
+ tunerService = mock(),
+ dumpManager = dumpManager,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 728c67a..b9918f1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -30,7 +30,7 @@
@SysUISingleton
class FakeShadeRepository @Inject constructor() : ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
- override val qsExpansion = _qsExpansion
+ @Deprecated("Use ShadeInteractor.qsExpansion instead") override val qsExpansion = _qsExpansion
private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress
@@ -59,11 +59,16 @@
private val _legacyIsQsExpanded = MutableStateFlow(false)
@Deprecated("Use ShadeInteractor instead") override val legacyIsQsExpanded = _legacyIsQsExpanded
+ @Deprecated("Use ShadeInteractor.isUserInteractingWithShade instead")
override val legacyLockscreenShadeTracking = MutableStateFlow(false)
private val _shadeMode = MutableStateFlow<ShadeMode>(ShadeMode.Single)
override val shadeMode: StateFlow<ShadeMode> = _shadeMode.asStateFlow()
+ private var _isDualShadeAlignedToBottom = false
+ override val isDualShadeAlignedToBottom
+ get() = _isDualShadeAlignedToBottom
+
@Deprecated("Use ShadeInteractor instead")
override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
_legacyIsQsExpanded.value = legacyIsQsExpanded
@@ -137,8 +142,12 @@
_legacyShadeExpansion.value = expandedFraction
}
- override fun setShadeMode(shadeMode: ShadeMode) {
- _shadeMode.value = shadeMode
+ override fun setShadeMode(mode: ShadeMode) {
+ _shadeMode.value = mode
+ }
+
+ fun setDualShadeAlignedToBottom(isAlignedToBottom: Boolean) {
+ _isDualShadeAlignedToBottom = isAlignedToBottom
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index 543d5b6..a00d2f4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -71,5 +71,6 @@
userSetupRepository = userSetupRepository,
userSwitcherInteractor = userSwitcherInteractor,
baseShadeInteractor = baseShadeInteractor,
+ shadeRepository = shadeRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
index 1ca3509..72a80d4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneViewModel
+import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.notificationsShadeSceneViewModel: NotificationsShadeSceneViewModel by
- Kosmos.Fixture { NotificationsShadeSceneViewModel() }
+ Kosmos.Fixture { NotificationsShadeSceneViewModel(shadeInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
index fec1028..8d4d547 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
@@ -19,11 +19,13 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.overlayShadeViewModel: OverlayShadeViewModel by
Kosmos.Fixture {
OverlayShadeViewModel(
applicationScope = applicationCoroutineScope,
sceneInteractor = sceneInteractor,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
index 4d81ea1..8adb26f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
import com.android.systemui.qs.ui.adapter.qsSceneAdapter
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneViewModel
+import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.quickSettingsShadeSceneViewModel: QuickSettingsShadeSceneViewModel by
Kosmos.Fixture {
@@ -31,5 +32,6 @@
tileGridViewModel = tileGridViewModel,
editModeViewModel = editModeViewModel,
qsSceneAdapter = qsSceneAdapter,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt
new file mode 100644
index 0000000..989c3a5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
+import com.android.systemui.qs.footerActionsController
+import com.android.systemui.qs.footerActionsViewModelFactory
+import com.android.systemui.qs.ui.adapter.qsSceneAdapter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
+
+val Kosmos.shadeSceneViewModel: ShadeSceneViewModel by
+ Kosmos.Fixture {
+ ShadeSceneViewModel(
+ applicationScope = applicationCoroutineScope,
+ shadeHeaderViewModel = shadeHeaderViewModel,
+ qsSceneAdapter = qsSceneAdapter,
+ brightnessMirrorViewModel = brightnessMirrorViewModel,
+ mediaCarouselInteractor = mediaCarouselInteractor,
+ shadeInteractor = shadeInteractor,
+ footerActionsViewModelFactory = footerActionsViewModelFactory,
+ footerActionsController = footerActionsController,
+ sceneInteractor = sceneInteractor,
+ unfoldTransitionInteractor = unfoldTransitionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/NotificationsDataKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/NotificationsDataKosmos.kt
deleted file mode 100644
index a61f7ec..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/NotificationsDataKosmos.kt
+++ /dev/null
@@ -1,23 +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.systemui.statusbar.notification.data
-
-import com.android.settingslib.statusbar.notification.data.repository.FakeNotificationsSoundPolicyRepository
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.notificationsSoundPolicyRepository by
- Kosmos.Fixture { FakeNotificationsSoundPolicyRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt
index 0614309..e6ca458 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt
@@ -16,12 +16,9 @@
package com.android.systemui.statusbar.notification.domain.interactor
-import com.android.settingslib.statusbar.notification.data.repository.FakeNotificationsSoundPolicyRepository
import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
import com.android.systemui.kosmos.Kosmos
-
-var Kosmos.notificationsSoundPolicyRepository by
- Kosmos.Fixture { FakeNotificationsSoundPolicyRepository() }
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
val Kosmos.notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor by
- Kosmos.Fixture { NotificationsSoundPolicyInteractor(notificationsSoundPolicyRepository) }
+ Kosmos.Fixture { NotificationsSoundPolicyInteractor(zenModeRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
new file mode 100644
index 0000000..16dc50f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.app.ActivityManager
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.os.UserHandle
+import android.provider.DeviceConfig
+import androidx.core.os.bundleOf
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.TestableDependency
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.media.dialog.MediaOutputDialogManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.DevicePolicyManagerWrapper
+import com.android.systemui.shared.system.PackageManagerWrapper
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.RankingBuilder
+import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
+import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl
+import com.android.systemui.statusbar.notification.icon.IconBuilder
+import com.android.systemui.statusbar.notification.icon.IconManager
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.CoordinateOnClickListener
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.SmartActionInflaterImpl
+import com.android.systemui.statusbar.policy.SmartReplyConstants
+import com.android.systemui.statusbar.policy.SmartReplyInflaterImpl
+import com.android.systemui.statusbar.policy.SmartReplyStateInflaterImpl
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent
+import com.android.systemui.util.Assert.runWithCurrentThreadAsMainThread
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.wmshell.BubblesManager
+import com.google.common.util.concurrent.MoreExecutors
+import com.google.common.util.concurrent.SettableFuture
+import java.util.Optional
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.test.TestScope
+import org.junit.Assert.assertTrue
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito
+
+class ExpandableNotificationRowBuilder(
+ private val context: Context,
+ dependency: TestableDependency,
+ private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
+) {
+
+ private val mMockLogger: ExpandableNotificationRowLogger
+ private val mStatusBarStateController: StatusBarStateController
+ private val mKeyguardBypassController: KeyguardBypassController
+ private val mGroupMembershipManager: GroupMembershipManager
+ private val mGroupExpansionManager: GroupExpansionManager
+ private val mHeadsUpManager: HeadsUpManager
+ private val mIconManager: IconManager
+ private val mContentBinder: NotificationRowContentBinder
+ private val mBindStage: RowContentBindStage
+ private val mBindPipeline: NotifBindPipeline
+ private val mBindPipelineEntryListener: NotifCollectionListener
+ private val mPeopleNotificationIdentifier: PeopleNotificationIdentifier
+ private val mOnUserInteractionCallback: OnUserInteractionCallback
+ private val mDismissibilityProvider: NotificationDismissibilityProvider
+ private val mSmartReplyController: SmartReplyController
+ private val mSmartReplyConstants: SmartReplyConstants
+ private val mTestScope: TestScope = TestScope()
+ private val mBgCoroutineContext = mTestScope.backgroundScope.coroutineContext
+ private val mMainCoroutineContext = mTestScope.coroutineContext
+ private val mFakeSystemClock = FakeSystemClock()
+ private val mMainExecutor = FakeExecutor(mFakeSystemClock)
+
+ init {
+ featureFlags.setDefault(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE)
+ featureFlags.setDefault(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)
+
+ dependency.injectTestDependency(FeatureFlags::class.java, featureFlags)
+ dependency.injectMockDependency(NotificationMediaManager::class.java)
+ dependency.injectMockDependency(NotificationShadeWindowController::class.java)
+ dependency.injectMockDependency(MediaOutputDialogManager::class.java)
+
+ mMockLogger = Mockito.mock(ExpandableNotificationRowLogger::class.java)
+ mStatusBarStateController = Mockito.mock(StatusBarStateController::class.java)
+ mKeyguardBypassController = Mockito.mock(KeyguardBypassController::class.java)
+ mGroupMembershipManager = GroupMembershipManagerImpl()
+ mSmartReplyController = Mockito.mock(SmartReplyController::class.java)
+
+ val dumpManager = DumpManager()
+ mGroupExpansionManager = GroupExpansionManagerImpl(dumpManager, mGroupMembershipManager)
+ mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java)
+ mIconManager =
+ IconManager(
+ Mockito.mock(CommonNotifCollection::class.java),
+ Mockito.mock(LauncherApps::class.java),
+ IconBuilder(context),
+ mTestScope,
+ mBgCoroutineContext,
+ mMainCoroutineContext,
+ )
+
+ mSmartReplyConstants =
+ SmartReplyConstants(
+ /* mainExecutor = */ mMainExecutor,
+ /* context = */ context,
+ /* deviceConfig = */ DeviceConfigProxyFake().apply {
+ setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
+ "true",
+ true
+ )
+ setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.SSIN_ENABLED,
+ "true",
+ true
+ )
+ setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
+ "false",
+ true
+ )
+ }
+ )
+ val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags)
+ val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java)
+ val smartReplyStateInflater =
+ SmartReplyStateInflaterImpl(
+ constants = mSmartReplyConstants,
+ activityManagerWrapper = ActivityManagerWrapper.getInstance(),
+ packageManagerWrapper = PackageManagerWrapper.getInstance(),
+ devicePolicyManagerWrapper = DevicePolicyManagerWrapper.getInstance(),
+ smartRepliesInflater =
+ SmartReplyInflaterImpl(
+ constants = mSmartReplyConstants,
+ keyguardDismissUtil = mock(),
+ remoteInputManager = remoteInputManager,
+ smartReplyController = mSmartReplyController,
+ context = context
+ ),
+ smartActionsInflater =
+ SmartActionInflaterImpl(
+ constants = mSmartReplyConstants,
+ activityStarter = mock(),
+ smartReplyController = mSmartReplyController,
+ headsUpManager = mHeadsUpManager
+ )
+ )
+ val notifLayoutInflaterFactoryProvider =
+ object : NotifLayoutInflaterFactory.Provider {
+ override fun provide(
+ row: ExpandableNotificationRow,
+ layoutType: Int
+ ): NotifLayoutInflaterFactory =
+ NotifLayoutInflaterFactory(row, layoutType, remoteViewsFactories)
+ }
+ val conversationProcessor =
+ ConversationNotificationProcessor(
+ mock(),
+ mock(),
+ )
+ mContentBinder =
+ if (NotificationRowContentBinderRefactor.isEnabled)
+ NotificationRowContentBinderImpl(
+ mock(),
+ remoteInputManager,
+ conversationProcessor,
+ mock(),
+ mock(),
+ mock(),
+ smartReplyStateInflater,
+ notifLayoutInflaterFactoryProvider,
+ mock(),
+ mock(),
+ )
+ else
+ NotificationContentInflater(
+ mock(),
+ remoteInputManager,
+ conversationProcessor,
+ mock(),
+ mock(),
+ smartReplyStateInflater,
+ notifLayoutInflaterFactoryProvider,
+ mock(),
+ mock(),
+ )
+ mContentBinder.setInflateSynchronously(true)
+ mBindStage =
+ RowContentBindStage(
+ mContentBinder,
+ mock(),
+ mock(),
+ )
+
+ val collection = Mockito.mock(CommonNotifCollection::class.java)
+
+ mBindPipeline =
+ NotifBindPipeline(
+ collection,
+ Mockito.mock(NotifBindPipelineLogger::class.java),
+ NotificationEntryProcessorFactoryExecutorImpl(mMainExecutor),
+ )
+ mBindPipeline.setStage(mBindStage)
+
+ val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
+ Mockito.verify(collection).addCollectionListener(collectionListenerCaptor.capture())
+ mBindPipelineEntryListener = collectionListenerCaptor.value
+ mPeopleNotificationIdentifier = Mockito.mock(PeopleNotificationIdentifier::class.java)
+ mOnUserInteractionCallback = Mockito.mock(OnUserInteractionCallback::class.java)
+ mDismissibilityProvider = Mockito.mock(NotificationDismissibilityProvider::class.java)
+ val mFutureDismissalRunnable = Mockito.mock(Runnable::class.java)
+ whenever(
+ mOnUserInteractionCallback.registerFutureDismissal(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt()
+ )
+ )
+ .thenReturn(mFutureDismissalRunnable)
+ }
+
+ private fun getNotifRemoteViewsFactoryContainer(
+ featureFlags: FeatureFlags,
+ ): NotifRemoteViewsFactoryContainer {
+ return NotifRemoteViewsFactoryContainerImpl(
+ featureFlags,
+ PrecomputedTextViewFactory(),
+ BigPictureLayoutInflaterFactory(),
+ NotificationOptimizedLinearLayoutFactory(),
+ { Mockito.mock(NotificationViewFlipperFactory::class.java) },
+ )
+ }
+
+ fun createRow(notification: Notification): ExpandableNotificationRow {
+ val channel =
+ NotificationChannel(
+ notification.channelId,
+ notification.channelId,
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ channel.isBlockable = true
+ val entry =
+ NotificationEntryBuilder()
+ .setPkg(PKG)
+ .setOpPkg(PKG)
+ .setId(123321)
+ .setUid(UID)
+ .setInitialPid(2000)
+ .setNotification(notification)
+ .setUser(USER_HANDLE)
+ .setPostTime(System.currentTimeMillis())
+ .setChannel(channel)
+ .build()
+
+ // it is for mitigating Rank building process.
+ if (notification.isConversationStyleNotification) {
+ val rb = RankingBuilder(entry.ranking)
+ rb.setIsConversation(true)
+ entry.ranking = rb.build()
+ }
+
+ return generateRow(entry, FLAG_CONTENT_VIEW_ALL)
+ }
+
+ private fun generateRow(
+ entry: NotificationEntry,
+ @InflationFlag extraInflationFlags: Int
+ ): ExpandableNotificationRow {
+ // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+ // set, but we do not want to override an existing value that is needed by a specific test.
+
+ val rowFuture: SettableFuture<ExpandableNotificationRow> = SettableFuture.create()
+ val rowInflaterTask =
+ RowInflaterTask(mFakeSystemClock, Mockito.mock(RowInflaterTaskLogger::class.java))
+ rowInflaterTask.inflate(context, null, entry, MoreExecutors.directExecutor()) { inflatedRow
+ ->
+ rowFuture.set(inflatedRow)
+ }
+ val row = rowFuture.get(1, TimeUnit.SECONDS)
+
+ entry.row = row
+ mIconManager.createIcons(entry)
+ mBindPipelineEntryListener.onEntryInit(entry)
+ mBindPipeline.manageRow(entry, row)
+ row.initialize(
+ entry,
+ Mockito.mock(RemoteInputViewSubcomponent.Factory::class.java),
+ APP_NAME,
+ entry.key,
+ mMockLogger,
+ mKeyguardBypassController,
+ mGroupMembershipManager,
+ mGroupExpansionManager,
+ mHeadsUpManager,
+ mBindStage,
+ Mockito.mock(OnExpandClickListener::class.java),
+ Mockito.mock(CoordinateOnClickListener::class.java),
+ FalsingManagerFake(),
+ mStatusBarStateController,
+ mPeopleNotificationIdentifier,
+ mOnUserInteractionCallback,
+ Optional.of(Mockito.mock(BubblesManager::class.java)),
+ Mockito.mock(NotificationGutsManager::class.java),
+ mDismissibilityProvider,
+ Mockito.mock(MetricsLogger::class.java),
+ Mockito.mock(NotificationChildrenContainerLogger::class.java),
+ Mockito.mock(ColorUpdateLogger::class.java),
+ mSmartReplyConstants,
+ mSmartReplyController,
+ featureFlags,
+ Mockito.mock(IStatusBarService::class.java)
+ )
+ row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
+ mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
+ inflateAndWait(entry)
+ return row
+ }
+
+ private fun inflateAndWait(entry: NotificationEntry) {
+ val countDownLatch = CountDownLatch(1)
+ mBindStage.requestRebind(entry) { en: NotificationEntry? -> countDownLatch.countDown() }
+ runWithCurrentThreadAsMainThread(mMainExecutor::runAllReady)
+ assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
+ }
+
+ companion object {
+ private const val APP_NAME = "appName"
+ private const val PKG = "com.android.systemui"
+ private const val UID = 1000
+ private val USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser())
+
+ private const val IS_CONVERSATION_FLAG = "test.isConversation"
+
+ private val Notification.isConversationStyleNotification
+ get() = extras.getBoolean(IS_CONVERSATION_FLAG, false)
+
+ fun markAsConversation(builder: Notification.Builder) {
+ builder.addExtras(bundleOf(IS_CONVERSATION_FLAG to true))
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepositoryKosmos.kt
new file mode 100644
index 0000000..84ef4b5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/data/repository/NotificationRowRepositoryKosmos.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.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
+import kotlinx.coroutines.flow.MutableStateFlow
+
+val Kosmos.fakeNotificationRowRepository by Fixture { FakeNotificationRowRepository() }
+
+class FakeNotificationRowRepository : NotificationRowRepository {
+ override val richOngoingContentModel = MutableStateFlow<RichOngoingContentModel?>(null)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractorKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractorKosmos.kt
index 6e11977..3a7d7ba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractorKosmos.kt
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.domain.interactor
+package com.android.systemui.statusbar.notification.row.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.panels.data.repository.infiniteGridSizeRepository
+import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
-val Kosmos.infiniteGridSizeInteractor by
- Kosmos.Fixture { InfiniteGridSizeInteractor(infiniteGridSizeRepository) }
+fun Kosmos.getNotificationRowInteractor(repository: NotificationRowRepository) =
+ NotificationRowInteractor(repository = repository)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelKosmos.kt
new file mode 100644
index 0000000..00f45b2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/TimerViewModelKosmos.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.getTimerViewModel(repository: NotificationRowRepository) =
+ TimerViewModel(
+ dumpManager = dumpManager,
+ rowInteractor = getNotificationRowInteractor(repository),
+ )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index f0eea38..a0e9303 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -18,10 +18,10 @@
import com.android.systemui.dump.dumpManager
import com.android.systemui.flags.featureFlagsClassic
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.ui.viewmodel.shadeSceneViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
val Kosmos.notificationsPlaceholderViewModel by Fixture {
@@ -29,7 +29,7 @@
dumpManager = dumpManager,
interactor = notificationStackAppearanceInteractor,
shadeInteractor = shadeInteractor,
+ shadeSceneViewModel = shadeSceneViewModel,
featureFlags = featureFlagsClassic,
- keyguardInteractor = keyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 299486f..ffd8aab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -86,6 +87,7 @@
primaryBouncerToLockscreenTransitionViewModel =
primaryBouncerToLockscreenTransitionViewModel,
aodBurnInViewModel = aodBurnInViewModel,
+ communalSceneInteractor = communalSceneInteractor,
unfoldTransitionInteractor = unfoldTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
index 1851c89..6574946 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
@@ -36,3 +36,14 @@
val Kosmos.mockSystemUIDialogFactory: SystemUIDialog.Factory by
Kosmos.Fixture { mock<SystemUIDialog.Factory>() }
+
+val Kosmos.systemUIDialogDotFactory by
+ Kosmos.Fixture {
+ SystemUIDialog.Factory(
+ applicationContext,
+ systemUIDialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogTransitionAnimator,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
index 16dab40..5aece1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
@@ -16,14 +16,7 @@
package com.android.systemui.statusbar.policy.data
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepositoryModule
-import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepositoryModule
import dagger.Module
-@Module(
- includes =
- [
- FakeDeviceProvisioningRepositoryModule::class,
- FakeZenModeRepositoryModule::class,
- ]
-)
+@Module(includes = [FakeDeviceProvisioningRepositoryModule::class])
object FakeStatusBarPolicyDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt
deleted file mode 100644
index c4d7867..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeZenModeRepository.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy.data.repository
-
-import android.app.NotificationManager.Policy
-import android.provider.Settings
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
-import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-
-@SysUISingleton
-class FakeZenModeRepository @Inject constructor() : ZenModeRepository {
- override val zenMode: MutableStateFlow<Int> = MutableStateFlow(Settings.Global.ZEN_MODE_OFF)
- override val consolidatedNotificationPolicy: MutableStateFlow<Policy?> =
- MutableStateFlow(
- Policy(
- /* priorityCategories = */ 0,
- /* priorityCallSenders = */ 0,
- /* priorityMessageSenders = */ 0,
- )
- )
-
- fun setSuppressedVisualEffects(suppressedVisualEffects: Int) {
- consolidatedNotificationPolicy.value =
- Policy(
- /* priorityCategories = */ 0,
- /* priorityCallSenders = */ 0,
- /* priorityMessageSenders = */ 0,
- /* suppressedVisualEffects = */ suppressedVisualEffects,
- )
- }
-}
-
-@Module
-interface FakeZenModeRepositoryModule {
- @Binds fun bindFake(fake: FakeZenModeRepository): ZenModeRepository
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryKosmos.kt
index 1ec7511..c7fda54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/ZenModeRepositoryKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy.data.repository
+import com.android.settingslib.statusbar.notification.data.repository.FakeZenModeRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 1f2ecb7..ed335f9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -39,7 +39,7 @@
// User id to represent a non system (human) user id. We presume this is the main user.
const val MAIN_USER_ID = 10
- private const val DEFAULT_SELECTED_USER = 0
+ const val DEFAULT_SELECTED_USER = 0
private val DEFAULT_SELECTED_USER_INFO =
UserInfo(
/* id= */ DEFAULT_SELECTED_USER,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyKosmos.kt
index d8af3fa..cf902ef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyKosmos.kt
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.util
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.fakeDeviceConfigProxy by Fixture { DeviceConfigProxyFake() }
+
+val Kosmos.deviceConfigProxy by Fixture<DeviceConfigProxy> { fakeDeviceConfigProxy }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
index 84ace7c..5fae38f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -18,6 +18,7 @@
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.Callback;
+import com.android.systemui.statusbar.policy.CastDevice;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index 21d59f0..fcea9e7b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -42,6 +42,7 @@
private val models: MutableMap<AudioStream, MutableStateFlow<AudioStreamModel>> = mutableMapOf()
private val lastAudibleVolumes: MutableMap<AudioStream, Int> = mutableMapOf()
+ private val deviceCategories: MutableMap<String, Int> = mutableMapOf()
private fun getAudioStreamModelState(
audioStream: AudioStream
@@ -103,4 +104,12 @@
override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
mutableRingerMode.value = mode
}
+
+ fun setBluetoothAudioDeviceCategory(bluetoothAddress: String, category: Int) {
+ deviceCategories[bluetoothAddress] = category
+ }
+
+ override suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int {
+ return deviceCategories[bluetoothAddress] ?: AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
index 141f242..83adc79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
@@ -18,9 +18,11 @@
import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
import android.graphics.drawable.Drawable
import android.graphics.drawable.TestStubDrawable
import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LeAudioProfile
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
@@ -59,11 +61,17 @@
whenever(name).thenReturn(deviceName)
whenever(address).thenReturn(deviceAddress)
}
+ val leAudioProfile =
+ mock<LeAudioProfile> {
+ whenever(profileId).thenReturn(BluetoothProfile.LE_AUDIO)
+ whenever(isEnabled(bluetoothDevice)).thenReturn(true)
+ }
val cachedBluetoothDevice: CachedBluetoothDevice = mock {
whenever(isHearingAidDevice).thenReturn(true)
whenever(address).thenReturn(deviceAddress)
whenever(device).thenReturn(bluetoothDevice)
whenever(name).thenReturn(deviceName)
+ whenever(profiles).thenReturn(listOf(leAudioProfile))
}
return mock<BluetoothMediaDevice> {
whenever(name).thenReturn(deviceName)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
index 95a7b9b..6a46d56 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.volume.panel.component.spatial.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerInteractor
+import com.android.systemui.volume.data.repository.audioRepository
import com.android.systemui.volume.domain.interactor.audioOutputInteractor
val Kosmos.spatialAudioComponentInteractor by
@@ -26,6 +28,8 @@
SpatialAudioComponentInteractor(
audioOutputInteractor,
spatializerInteractor,
+ audioRepository,
+ backgroundCoroutineContext,
testScope.backgroundScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
index b2b19de..e6b52f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
@@ -20,6 +20,7 @@
import com.android.internal.logging.uiEventLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
+import com.android.systemui.volume.shared.volumePanelLogger
import kotlinx.coroutines.CoroutineScope
val Kosmos.audioStreamSliderViewModelFactory by
@@ -36,6 +37,7 @@
applicationContext,
audioVolumeInteractor,
uiEventLogger,
+ volumePanelLogger,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/shared/VolumePanelLoggerKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/shared/VolumePanelLoggerKosmos.kt
index d8af3fa..3a7574d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/shared/VolumePanelLoggerKosmos.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.volume.shared
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
-val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
+val Kosmos.volumePanelLogger by Kosmos.Fixture { VolumePanelLogger(logcatLogBuffer()) }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 7f542d1..994bdb5 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -310,6 +310,14 @@
// Package: android
NOTE_USB_UVC = 75;
+ // Inform the user about adaptive notifications
+ // Package: com.android.systemui
+ NOTE_ADAPTIVE_NOTIFICATIONS = 76;
+
+ // Warn the user that the device's Headless System User Mode status doesn't match the build's.
+ // Package: android
+ NOTE_WRONG_HSUM_STATUS = 77;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 48bc803..e2eb09f 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -231,6 +231,12 @@
}
filegroup {
+ name: "ravenwood-framework-jarjar-rules",
+ srcs: ["texts/ravenwood-framework-jarjar-rules.txt"],
+ visibility: ["//frameworks/base"],
+}
+
+filegroup {
name: "ravenwood-services-jarjar-rules",
srcs: ["texts/ravenwood-services-jarjar-rules.txt"],
visibility: ["//frameworks/base"],
@@ -268,11 +274,87 @@
src: "scripts/ravenwood-stats-checker.sh",
test_suites: ["general-tests"],
data: [
- ":framework-minus-apex.ravenwood.stats",
- ":framework-minus-apex.ravenwood.apis",
- ":framework-minus-apex.ravenwood.keep_all",
- ":services.core.ravenwood.stats",
- ":services.core.ravenwood.apis",
- ":services.core.ravenwood.keep_all",
+ ":framework-minus-apex.ravenwood-base{hoststubgen_framework-minus-apex_stats.csv}",
+ ":framework-minus-apex.ravenwood-base{hoststubgen_framework-minus-apex_apis.csv}",
+ ":framework-minus-apex.ravenwood-base{hoststubgen_framework-minus-apex_keep_all.txt}",
+ ":framework-minus-apex.ravenwood-base{hoststubgen_framework-minus-apex_dump.txt}",
+ ":services.core.ravenwood-base{hoststubgen_services.core_stats.csv}",
+ ":services.core.ravenwood-base{hoststubgen_services.core_apis.csv}",
+ ":services.core.ravenwood-base{hoststubgen_services.core_keep_all.txt}",
+ ":services.core.ravenwood-base{hoststubgen_services.core_dump.txt}",
+ ],
+}
+
+java_library {
+ name: "services.fakes.ravenwood-jarjar",
+ installable: false,
+ srcs: [":services.fakes-sources"],
+ libs: [
+ "ravenwood-framework",
+ "services.core.ravenwood",
+ ],
+ jarjar_rules: ":ravenwood-services-jarjar-rules",
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "mockito-ravenwood-prebuilt",
+ installable: false,
+ static_libs: [
+ "mockito-robolectric-prebuilt",
+ ],
+}
+
+java_library {
+ name: "inline-mockito-ravenwood-prebuilt",
+ installable: false,
+ static_libs: [
+ "inline-mockito-robolectric-prebuilt",
+ ],
+}
+
+android_ravenwood_libgroup {
+ name: "ravenwood-runtime",
+ libs: [
+ "100-framework-minus-apex.ravenwood",
+ "200-kxml2-android",
+
+ "ravenwood-runtime-common-ravenwood",
+
+ "android.test.mock.ravenwood",
+ "ravenwood-helper-runtime",
+ "hoststubgen-helper-runtime.ravenwood",
+ "services.core.ravenwood-jarjar",
+ "services.fakes.ravenwood-jarjar",
+
+ // Provide runtime versions of utils linked in below
+ "junit",
+ "truth",
+ "flag-junit",
+ "ravenwood-framework",
+ "ravenwood-junit-impl",
+ "ravenwood-junit-impl-flag",
+ "mockito-ravenwood-prebuilt",
+ "inline-mockito-ravenwood-prebuilt",
+
+ // It's a stub, so it should be towards the end.
+ "z00-all-updatable-modules-system-stubs",
+ ],
+ jni_libs: [
+ "libandroid_runtime",
+ "libravenwood_runtime",
+ ],
+}
+
+android_ravenwood_libgroup {
+ name: "ravenwood-utils",
+ libs: [
+ "junit",
+ "truth",
+ "flag-junit",
+ "ravenwood-framework",
+ "ravenwood-junit",
+ "mockito-ravenwood-prebuilt",
+ "inline-mockito-ravenwood-prebuilt",
],
}
diff --git a/ravenwood/bivalenttest/Android.bp b/ravenwood/bivalenttest/Android.bp
index 2d94894..06cf08e6 100644
--- a/ravenwood/bivalenttest/Android.bp
+++ b/ravenwood/bivalenttest/Android.bp
@@ -38,6 +38,9 @@
"androidx.annotation_annotation",
"androidx.test.ext.junit",
"androidx.test.rules",
+
+ // To make sure it won't cause VerifyError (b/324063814)
+ "platformprotosnano",
],
srcs: [
"test/**/*.java",
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index 43b61a4..36601bd 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -22,10 +22,12 @@
stats=$out_dir/ravenwood-stats-all.csv
apis=$out_dir/ravenwood-apis-all.csv
keep_all_dir=$out_dir/ravenwood-keep-all/
+dump_dir=$out_dir/ravenwood-dump/
rm -fr $out_dir
mkdir -p $out_dir
mkdir -p $keep_all_dir
+mkdir -p $dump_dir
# Where the input files are.
path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/
@@ -85,4 +87,8 @@
cp *keep_all.txt $keep_all_dir
echo "Keep all files created at:"
-find $keep_all_dir -type f
\ No newline at end of file
+find $keep_all_dir -type f
+
+cp *dump.txt $dump_dir
+echo "Dump files created at:"
+find $dump_dir -type f
\ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-framework-jarjar-rules.txt b/ravenwood/texts/ravenwood-framework-jarjar-rules.txt
new file mode 100644
index 0000000..2eeb904
--- /dev/null
+++ b/ravenwood/texts/ravenwood-framework-jarjar-rules.txt
@@ -0,0 +1,2 @@
+# To avoid VerifyError on nano proto files (b/324063814)
+rule com.**.nano.** devicenano.@0
diff --git a/services/OWNERS b/services/OWNERS
index 3ce5ae1..69e4e24 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1,7 +1,7 @@
per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
# art-team@ manages the system server profile
-per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file art-profile* = file:platform/art:main:/OWNERS_boot_profile
per-file java/com/android/server/HsumBootUserInitializer.java = file:/MULTIUSER_OWNERS
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 9353150..b4efae3 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -11,6 +11,16 @@
}
flag {
+ name: "always_allow_observing_touch_events"
+ namespace: "accessibility"
+ description: "Always allows InputFilter observing SOURCE_TOUCHSCREEN events, even if touch exploration is enabled."
+ bug: "344604959"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "resettable_dynamic_properties"
namespace: "accessibility"
description: "Maintains initial copies of a11yServiceInfo dynamic properties so they can reset on disconnect."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 5fb60e7..f9196f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -1087,21 +1087,15 @@
}
}
- private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
- // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
- // touch exploration.
- if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
- && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
- return false;
- }
- final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
- return (mCombinedGenericMotionEventSources
- & mCombinedMotionEventObservedSources
- & eventSourceWithoutClass)
- != 0;
- }
-
private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
+ if (Flags.alwaysAllowObservingTouchEvents()) {
+ final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
+ if (isTouchEvent && !canShareGenericTouchEvent()) {
+ return false;
+ }
+ final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+ return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
+ }
// Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
// touch exploration.
if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
@@ -1112,6 +1106,36 @@
return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
}
+ private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
+ if (Flags.alwaysAllowObservingTouchEvents()) {
+ final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+ return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0;
+ }
+ // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
+ // touch exploration.
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
+ && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ return false;
+ }
+ final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+ return (mCombinedGenericMotionEventSources
+ & mCombinedMotionEventObservedSources
+ & eventSourceWithoutClass)
+ != 0;
+ }
+
+ private boolean canShareGenericTouchEvent() {
+ if ((mCombinedMotionEventObservedSources & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+ // Share touch events if a MotionEvent-observing service wants them.
+ return true;
+ }
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) == 0) {
+ // Share touch events if touch exploration is not enabled.
+ return true;
+ }
+ return false;
+ }
+
public void setCombinedGenericMotionEventSources(int sources) {
mCombinedGenericMotionEventSources = sources;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4f9db8b..1dc3fb4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -47,6 +47,11 @@
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -844,14 +849,17 @@
userState.mEnabledServices, userState.mUserId);
}
- boolean buttonTargetsChanged = userState.mAccessibilityButtonTargets.removeIf(
+ // Remove any button targets that match any stopped continuous services
+ Set<String> buttonTargets = userState.getShortcutTargetsLocked(SOFTWARE);
+ boolean buttonTargetsChanged = buttonTargets.removeIf(
target -> continuousServicePackages.stream().anyMatch(
pkg -> Objects.equals(target, pkg)));
if (buttonTargetsChanged) {
+ userState.updateShortcutTargetsLocked(buttonTargets, SOFTWARE);
persistColonDelimitedSetToSettingLocked(
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ ShortcutUtils.convertToKey(SOFTWARE),
userState.mUserId,
- userState.mAccessibilityButtonTargets, str -> str);
+ buttonTargets, str -> str);
}
return enabledServicesChanged || buttonTargetsChanged;
@@ -923,25 +931,11 @@
newValue, restoredFromSdk);
}
}
- case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> {
- synchronized (mLock) {
- restoreAccessibilityButtonTargetsLocked(
- previousValue, newValue);
- }
- }
- case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> {
- if (!android.view.accessibility.Flags.a11yQsShortcut()) {
- return;
- }
- restoreAccessibilityQsTargets(newValue);
- }
- case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> {
- if (!android.view.accessibility.Flags
- .restoreA11yShortcutTargetService()) {
- return;
- }
- restoreAccessibilityShortcutTargetService(previousValue, newValue);
- }
+ case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
+ restoreShortcutTargets(newValue,
+ ShortcutUtils.convertToType(which));
}
}
}
@@ -1040,7 +1034,7 @@
}
persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
userState.mUserId, targetsFromSetting, str -> str);
- readAccessibilityButtonTargetsLocked(userState);
+ readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
onUserStateChangedLocked(userState);
}
@@ -1615,7 +1609,7 @@
}
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::performAccessibilityShortcutInternal, this,
- displayId, UserShortcutType.SOFTWARE, targetName));
+ displayId, SOFTWARE, targetName));
}
/**
@@ -1720,12 +1714,12 @@
}
// Turn on/off a11y qs shortcut for the a11y features based on the change in QS Panel
if (!a11yFeaturesToEnable.isEmpty()) {
- enableShortcutForTargets(/* enable= */ true, UserShortcutType.QUICK_SETTINGS,
+ enableShortcutForTargets(/* enable= */ true, QUICK_SETTINGS,
a11yFeaturesToEnable, userId);
}
if (!a11yFeaturesToRemove.isEmpty()) {
- enableShortcutForTargets(/* enable= */ false, UserShortcutType.QUICK_SETTINGS,
+ enableShortcutForTargets(/* enable= */ false, QUICK_SETTINGS,
a11yFeaturesToRemove, userId);
}
}
@@ -2057,100 +2051,74 @@
}
/**
- * User could enable accessibility services and configure accessibility button during the SUW.
- * Merges current value of accessibility button settings into the restored one to make sure
- * user's preferences of accessibility button updated in SUW are not lost.
- *
- * Called only during settings restore; currently supports only the owner user
- * TODO: http://b/22388012
- */
- void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting,
- /* doMerge = */false);
- readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting,
- /* doMerge = */true);
-
- final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- userState.mAccessibilityButtonTargets.clear();
- userState.mAccessibilityButtonTargets.addAll(targetsFromSetting);
- persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
- UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str);
-
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- onUserStateChangedLocked(userState);
- }
-
- /**
* User could configure accessibility shortcut during the SUW before restoring user data.
* Merges the current value and the new value to make sure we don't lost the setting the user's
- * preferences of accessibility qs shortcut updated in SUW are not lost.
- *
- * Called only during settings restore; currently supports only the owner user
+ * preferences of accessibility shortcut updated in SUW are not lost.
+ * Called only during settings restore; currently supports only the owner user.
+ * <P>
+ * Throws an exception if used with {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}.
+ * </P>
* TODO: http://b/22388012
*/
- private void restoreAccessibilityQsTargets(String newValue) {
+ private void restoreShortcutTargets(String newValue,
+ @UserShortcutType int shortcutType) {
+ assertNoTapShortcut(shortcutType);
+ if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
+ return;
+ }
+ if (shortcutType == HARDWARE
+ && !android.view.accessibility.Flags.restoreA11yShortcutTargetService()) {
+ return;
+ }
+
synchronized (mLock) {
final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- final Set<String> mergedTargets = userState.getA11yQsTargets();
- readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
- /* doMerge = */ true);
+ final Set<String> mergedTargets = (shortcutType == HARDWARE)
+ ? new ArraySet<>(ShortcutUtils.getShortcutTargetsFromSettings(
+ mContext, shortcutType, userState.mUserId))
+ : userState.getShortcutTargetsLocked(shortcutType);
- userState.updateA11yQsTargetLocked(mergedTargets);
- persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+ if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()
+ && shortcutType == HARDWARE) {
+ final String defaultService =
+ mContext.getString(R.string.config_defaultAccessibilityService);
+ final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
+ ? null : ComponentName.unflattenFromString(defaultService);
+ boolean shouldClearDefaultService = defaultServiceComponent != null
+ && !stringSetContainsComponentName(mergedTargets, defaultServiceComponent);
+ readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
+ /* doMerge = */ true);
+
+ if (shouldClearDefaultService && stringSetContainsComponentName(
+ mergedTargets, defaultServiceComponent)) {
+ Slog.i(LOG_TAG, "Removing default service " + defaultService
+ + " from restore of "
+ + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+ mergedTargets.removeIf(str ->
+ defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
+ }
+ if (mergedTargets.isEmpty()) {
+ return;
+ }
+ } else {
+ readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
+ /* doMerge = */ true);
+ }
+
+ userState.updateShortcutTargetsLocked(mergedTargets, shortcutType);
+ persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(shortcutType),
UserHandle.USER_SYSTEM, mergedTargets, str -> str);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
onUserStateChangedLocked(userState);
}
}
- /**
- * Merges the old and restored value of
- * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
- *
- * <p>Also clears out {@link R.string#config_defaultAccessibilityService} from
- * the merged set if it was not present before restoring.
- */
- private void restoreAccessibilityShortcutTargetService(
- String oldValue, String restoredValue) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedStringToSet(oldValue, str -> str,
- targetsFromSetting, /*doMerge=*/false);
- final String defaultService =
- mContext.getString(R.string.config_defaultAccessibilityService);
- final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
- ? null : ComponentName.unflattenFromString(defaultService);
- boolean shouldClearDefaultService = defaultServiceComponent != null
- && !stringSetContainsComponentName(targetsFromSetting, defaultServiceComponent);
- readColonDelimitedStringToSet(restoredValue, str -> str,
- targetsFromSetting, /*doMerge=*/true);
- if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()) {
- if (shouldClearDefaultService && stringSetContainsComponentName(
- targetsFromSetting, defaultServiceComponent)) {
- Slog.i(LOG_TAG, "Removing default service " + defaultService
- + " from restore of "
- + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
- targetsFromSetting.removeIf(str ->
- defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
- }
- if (targetsFromSetting.isEmpty()) {
- return;
- }
- }
- synchronized (mLock) {
- final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- final Set<String> shortcutTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
- shortcutTargets.clear();
- shortcutTargets.addAll(targetsFromSetting);
- persistColonDelimitedSetToSettingLocked(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- UserHandle.USER_SYSTEM, targetsFromSetting, str -> str);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- onUserStateChangedLocked(userState);
- }
+ private String getRawShortcutSetting(int userId, @UserShortcutType int shortcutType) {
+ return Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ ShortcutUtils.convertToKey(shortcutType), userId);
}
+
/**
* Returns {@code true} if the set contains the provided non-null {@link ComponentName}.
*
@@ -2263,7 +2231,7 @@
private void showAccessibilityTargetsSelection(int displayId,
@UserShortcutType int shortcutType) {
final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
- final String chooserClassName = (shortcutType == UserShortcutType.HARDWARE)
+ final String chooserClassName = (shortcutType == HARDWARE)
? AccessibilityShortcutChooserActivity.class.getName()
: AccessibilityButtonChooserActivity.class.getName();
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
@@ -3116,9 +3084,9 @@
scheduleUpdateInputFilter(userState);
updateRelevantEventsLocked(userState);
scheduleUpdateClientsIfNeededLocked(userState, forceUpdate);
- updateAccessibilityShortcutKeyTargetsLocked(userState);
- updateAccessibilityButtonTargetsLocked(userState);
- updateAccessibilityQsTargetsLocked(userState);
+ updateAccessibilityShortcutTargetsLocked(userState, HARDWARE);
+ updateAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
+ updateAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
// Update the capabilities before the mode because we will check the current mode is
// invalid or not..
updateMagnificationCapabilitiesSettingsChangeLocked(userState);
@@ -3236,9 +3204,9 @@
somethingChanged |= readAudioDescriptionEnabledSettingLocked(userState);
somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
- somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
- somethingChanged |= readAccessibilityQsTargetsLocked(userState);
- somethingChanged |= readAccessibilityButtonTargetsLocked(userState);
+ somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, HARDWARE);
+ somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
+ somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
@@ -3386,60 +3354,34 @@
userState.setSendMotionEventsEnabled(sendMotionEvents);
}
- private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
- final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
+ /**
+ * Throws an exception for {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} types.
+ */
+ private boolean readAccessibilityShortcutTargetsLocked(AccessibilityUserState userState,
+ @UserShortcutType int shortcutType) {
+ assertNoTapShortcut(shortcutType);
+ final String settingValue = getRawShortcutSetting(userState.mUserId, shortcutType);
final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
- // Fall back to device's default a11y service, only when setting is never updated.
- if (settingValue == null) {
+ // If dealing with an empty hardware shortcut, fall back to the default value.
+ if (shortcutType == HARDWARE && settingValue == null) {
final String defaultService = mContext.getString(
R.string.config_defaultAccessibilityService);
if (!TextUtils.isEmpty(defaultService)) {
- targetsFromSetting.add(defaultService);
+ // Convert to component name to reformat the target if it has a relative path.
+ ComponentName name = ComponentName.unflattenFromString(defaultService);
+ if (name != null) {
+ targetsFromSetting.add(name.flattenToString());
+ }
}
+ } else {
+ readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
}
- final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
- if (targetsFromSetting.equals(currentTargets)) {
- return false;
+ if (userState.updateShortcutTargetsLocked(targetsFromSetting, shortcutType)) {
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+ return true;
}
- currentTargets.clear();
- currentTargets.addAll(targetsFromSetting);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- return true;
- }
-
- private boolean readAccessibilityQsTargetsLocked(AccessibilityUserState userState) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
- userState.mUserId, str -> str, targetsFromSetting);
-
- final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
- if (targetsFromSetting.equals(currentTargets)) {
- return false;
- }
- userState.updateA11yQsTargetLocked(targetsFromSetting);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- return true;
- }
-
- private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
- final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
- userState.mUserId, str -> str, targetsFromSetting);
-
- final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
- if (targetsFromSetting.equals(currentTargets)) {
- return false;
- }
- currentTargets.clear();
- currentTargets.addAll(targetsFromSetting);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- return true;
+ return false;
}
private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) {
@@ -3480,31 +3422,6 @@
return false;
}
- /**
- * Check if the target that will be enabled by the accessibility shortcut key is installed.
- * If it isn't, remove it from the list and associated setting so a side loaded service can't
- * spoof the package name of the default service.
- */
- private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) {
- final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
- final int lastSize = currentTargets.size();
- if (lastSize == 0) {
- return;
- }
- currentTargets.removeIf(
- name -> !userState.isShortcutTargetInstalledLocked(name));
- if (lastSize == currentTargets.size()) {
- return;
- }
-
- // Update setting key with new value.
- persistColonDelimitedSetToSettingLocked(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- userState.mUserId, currentTargets, str -> str);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- }
-
private boolean canRequestAndRequestsTouchExplorationLocked(
AccessibilityServiceConnection service, AccessibilityUserState userState) {
// Service not ready or cannot request the feature - well nothing to do.
@@ -3663,35 +3580,56 @@
}
/**
- * 1) Update accessibility button availability to accessibility services.
- * 2) Check if the target that will be enabled by the accessibility button is installed.
- * If it isn't, remove it from the list and associated setting so a side loaded service can't
- * spoof the package name of the default service.
+ * Adds or removes shortcut targets based on other attributes in the UserState.
+ * <P></P>
+ * Any target is removed if it is no longer installed on device.
+ * For software shortcuts,
+ * the function also updates the availability of the a11y button for services that request it.
+ * For quick settings shortcuts,
+ * targets are added if their service is enabled and has a corresponding QS panel.
*/
- private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
- // Update accessibility button availability.
- for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
- final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
- if (service.mRequestAccessibilityButton) {
- service.notifyAccessibilityButtonAvailabilityChangedLocked(
- service.isAccessibilityButtonAvailableLocked(userState));
+ private void updateAccessibilityShortcutTargetsLocked(
+ AccessibilityUserState userState, @UserShortcutType int shortcutType) {
+ if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
+ return;
+ }
+ if (shortcutType == SOFTWARE) {
+ // Update accessibility button availability.
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton) {
+ service.notifyAccessibilityButtonAvailabilityChangedLocked(
+ service.isAccessibilityButtonAvailableLocked(userState));
+ }
}
}
+ // Remove targets that are no longer installed on device.
final Set<String> currentTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
- final int lastSize = currentTargets.size();
- if (lastSize == 0) {
- return;
- }
+ userState.getShortcutTargetsLocked(shortcutType);
currentTargets.removeIf(
name -> !userState.isShortcutTargetInstalledLocked(name));
- if (lastSize == currentTargets.size()) {
+
+ if (shortcutType == QUICK_SETTINGS) {
+ // Add the target if the a11y service is enabled and the tile exist in QS panel
+ Set<ComponentName> enabledServices = userState.getEnabledServicesLocked();
+ Map<ComponentName, ComponentName> a11yFeatureToTileService =
+ userState.getA11yFeatureToTileService();
+ Set<ComponentName> currentA11yTilesInQsPanel = userState.getA11yQsTilesInQsPanel();
+ for (ComponentName enabledService : enabledServices) {
+ ComponentName tileService =
+ a11yFeatureToTileService.getOrDefault(enabledService, null);
+ if (tileService != null && currentA11yTilesInQsPanel.contains(tileService)) {
+ currentTargets.add(enabledService.flattenToString());
+ }
+ }
+ }
+ // Update targets in userState
+ if (!userState.updateShortcutTargetsLocked(currentTargets, shortcutType)) {
return;
}
-
- // Update setting key with new value.
- persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ // If there was a change to the targets in userState, save the new targets to Settings.
+ persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(shortcutType),
userState.mUserId, currentTargets, str -> str);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
}
@@ -3719,8 +3657,7 @@
return;
}
final Set<String> buttonTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
- int lastSize = buttonTargets.size();
+ userState.getShortcutTargetsLocked(SOFTWARE);
buttonTargets.removeIf(name -> {
if (packageName != null && name != null && !name.contains(packageName)) {
return false;
@@ -3752,13 +3689,11 @@
}
return false;
});
- boolean changed = (lastSize != buttonTargets.size());
- lastSize = buttonTargets.size();
final Set<String> shortcutKeyTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
+ userState.getShortcutTargetsLocked(HARDWARE);
final Set<String> qsShortcutTargets =
- userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
+ userState.getShortcutTargetsLocked(QUICK_SETTINGS);
userState.mEnabledServices.forEach(componentName -> {
if (packageName != null && componentName != null
&& !packageName.equals(componentName.getPackageName())) {
@@ -3790,8 +3725,7 @@
+ " should be assign to the button or shortcut.");
buttonTargets.add(serviceName);
});
- changed |= (lastSize != buttonTargets.size());
- if (!changed) {
+ if (!userState.updateShortcutTargetsLocked(buttonTargets, SOFTWARE)) {
return;
}
@@ -3802,50 +3736,6 @@
}
/**
- * Update the Settings.Secure.ACCESSIBILITY_QS_TARGETS so that it only contains valid content,
- * and a side loaded service can't spoof the package name of the default service.
- * <p>
- * 1. Remove the target if the target is no longer installed on the device <br/>
- * 2. Add the target if the target is enabled and the target's tile is in the QS Panel <br/>
- * </p>
- */
- private void updateAccessibilityQsTargetsLocked(AccessibilityUserState userState) {
- if (!android.view.accessibility.Flags.a11yQsShortcut()) {
- return;
- }
-
- final Set<String> targets =
- userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
-
- // Removes the targets that are no longer installed on the device.
- boolean somethingChanged = targets.removeIf(
- name -> !userState.isShortcutTargetInstalledLocked(name));
- // Add the target if the a11y service is enabled and the tile exist in QS panel
- Set<ComponentName> enabledServices = userState.getEnabledServicesLocked();
- Map<ComponentName, ComponentName> a11yFeatureToTileService =
- userState.getA11yFeatureToTileService();
- Set<ComponentName> currentA11yTilesInQsPanel = userState.getA11yQsTilesInQsPanel();
- for (ComponentName enabledService : enabledServices) {
- ComponentName tileService =
- a11yFeatureToTileService.getOrDefault(enabledService, null);
- if (tileService != null && currentA11yTilesInQsPanel.contains(tileService)) {
- somethingChanged |= targets.add(enabledService.flattenToString());
- }
- }
-
- if (!somethingChanged) {
- return;
- }
- userState.updateA11yQsTargetLocked(targets);
-
- // Update setting key with new value.
- persistColonDelimitedSetToSettingLocked(
- Settings.Secure.ACCESSIBILITY_QS_TARGETS,
- userState.mUserId, targets, str -> str);
- scheduleNotifyClientsOfServicesStateChangeLocked(userState);
- }
-
- /**
* Remove the shortcut target for the unbound service which is requesting accessibility button
* and targeting sdk > Q from the accessibility button and shortcut.
*
@@ -3862,14 +3752,14 @@
final List<Pair<Integer, String>> shortcutTypeAndShortcutSetting = new ArrayList<>(3);
shortcutTypeAndShortcutSetting.add(
- new Pair<>(UserShortcutType.HARDWARE,
+ new Pair<>(HARDWARE,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
shortcutTypeAndShortcutSetting.add(
- new Pair<>(UserShortcutType.SOFTWARE,
+ new Pair<>(SOFTWARE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS));
if (android.view.accessibility.Flags.a11yQsShortcut()) {
shortcutTypeAndShortcutSetting.add(
- new Pair<>(UserShortcutType.QUICK_SETTINGS,
+ new Pair<>(QUICK_SETTINGS,
Settings.Secure.ACCESSIBILITY_QS_TARGETS));
}
@@ -3883,7 +3773,7 @@
shortcutSettingName,
userState.mUserId, currentTargets, str -> str);
- if (shortcutType != UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType != QUICK_SETTINGS) {
continue;
}
@@ -3959,7 +3849,8 @@
*/
@EnforcePermission(MANAGE_ACCESSIBILITY)
@Override
- public void performAccessibilityShortcut(String targetName) {
+ public void performAccessibilityShortcut(
+ int displayId, @UserShortcutType int shortcutType, String targetName) {
performAccessibilityShortcut_enforcePermission();
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut",
@@ -3968,7 +3859,7 @@
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::performAccessibilityShortcutInternal, this,
- Display.DEFAULT_DISPLAY, UserShortcutType.HARDWARE, targetName));
+ displayId, shortcutType, targetName));
}
/**
@@ -4115,7 +4006,7 @@
final boolean requestA11yButton = (installedServiceInfo.flags
& FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
// Turns on / off the accessibility service
- if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == UserShortcutType.HARDWARE)
+ if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == HARDWARE)
|| (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
if (serviceConnection == null) {
logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
@@ -4129,7 +4020,7 @@
}
return true;
}
- if (shortcutType == UserShortcutType.HARDWARE && targetSdk > Build.VERSION_CODES.Q
+ if (shortcutType == HARDWARE && targetSdk > Build.VERSION_CODES.Q
&& requestA11yButton) {
if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
@@ -4222,7 +4113,7 @@
validNewTargets = newTargets;
// filter out targets that doesn't have qs shortcut
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType == QUICK_SETTINGS) {
validNewTargets = newTargets.stream().filter(target -> {
ComponentName targetComponent = ComponentName.unflattenFromString(target);
return featureToTileMap.containsKey(targetComponent);
@@ -4240,10 +4131,10 @@
/* defaultEmptyString= */ ""
);
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType == QUICK_SETTINGS) {
int numOfFeatureChanged = Math.abs(currentTargets.size() - validNewTargets.size());
logMetricForQsShortcutConfiguration(enable, numOfFeatureChanged);
- userState.updateA11yQsTargetLocked(validNewTargets);
+ userState.updateShortcutTargetsLocked(validNewTargets, QUICK_SETTINGS);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
onUserStateChangedLocked(userState);
}
@@ -4257,7 +4148,7 @@
}
// Add or Remove tile in QS Panel
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+ if (shortcutType == QUICK_SETTINGS) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::updateA11yTileServicesInQuickSettingsPanel,
this, validNewTargets, currentTargets, userId));
@@ -4266,7 +4157,7 @@
if (!enable) {
return;
}
- if (shortcutType == UserShortcutType.HARDWARE) {
+ if (shortcutType == HARDWARE) {
skipVolumeShortcutDialogTimeoutRestriction(userId);
if (com.android.server.accessibility.Flags.enableHardwareShortcutDisablesWarning()) {
persistIntToSetting(
@@ -4275,7 +4166,7 @@
AccessibilityShortcutController.DialogStatus.SHOWN
);
}
- } else if (shortcutType == UserShortcutType.SOFTWARE) {
+ } else if (shortcutType == SOFTWARE) {
// Update the A11y FAB size to large when the Magnification shortcut is
// enabled and the user hasn't changed the floating button size
if (shortcutTargets.contains(MAGNIFICATION_CONTROLLER_NAME)
@@ -4445,7 +4336,7 @@
final AccessibilityUserState userState = getCurrentUserStateLocked();
final ArrayList<String> shortcutTargets = new ArrayList<>(
userState.getShortcutTargetsLocked(shortcutType));
- if (shortcutType != UserShortcutType.SOFTWARE) {
+ if (shortcutType != SOFTWARE) {
return shortcutTargets;
}
// Adds legacy a11y services requesting a11y button into the list.
@@ -4461,6 +4352,7 @@
shortcutTargets.add(serviceName);
}
}
+ userState.updateShortcutTargetsLocked(Set.copyOf(shortcutTargets), shortcutType);
return shortcutTargets;
}
}
@@ -5672,7 +5564,7 @@
|| mShowImeWithHardKeyboardUri.equals(uri)) {
userState.reconcileSoftKeyboardModeWithSettingsLocked();
} else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
- if (readAccessibilityShortcutKeySettingLocked(userState)) {
+ if (readAccessibilityShortcutTargetsLocked(userState, HARDWARE)) {
onUserStateChangedLocked(userState);
}
} else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
@@ -5680,7 +5572,7 @@
onUserStateChangedLocked(userState);
}
} else if (mAccessibilityButtonTargetsUri.equals(uri)) {
- if (readAccessibilityButtonTargetsLocked(userState)) {
+ if (readAccessibilityShortcutTargetsLocked(userState, SOFTWARE)) {
onUserStateChangedLocked(userState);
}
} else if (mUserNonInteractiveUiTimeoutUri.equals(uri)
@@ -6505,4 +6397,10 @@
String metricId = enable ? METRIC_ID_QS_SHORTCUT_ADD : METRIC_ID_QS_SHORTCUT_REMOVE;
Counter.logIncrementWithUid(metricId, Binder.getCallingUid(), numOfFeatures);
}
+
+ private void assertNoTapShortcut(@UserShortcutType int shortcutType) {
+ if ((shortcutType & (TRIPLETAP | TWOFINGER_DOUBLETAP)) != 0) {
+ throw new IllegalArgumentException("Tap shortcuts are not supported.");
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index a37a184..710bae9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -98,9 +98,9 @@
final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
- final ArraySet<String> mAccessibilityShortcutKeyTargets = new ArraySet<>();
+ private final ArraySet<String> mAccessibilityShortcutKeyTargets = new ArraySet<>();
- final ArraySet<String> mAccessibilityButtonTargets = new ArraySet<>();
+ private final ArraySet<String> mAccessibilityButtonTargets = new ArraySet<>();
private final ArraySet<String> mAccessibilityQsTargets = new ArraySet<>();
/**
@@ -771,18 +771,23 @@
/**
* Returns a set which contains the flattened component names and the system class names
- * assigned to the given shortcut.
+ * assigned to the given shortcut. The set is a defensive copy. To apply any changes to the set,
+ * use {@link #updateShortcutTargetsLocked(Set, int)}
*
* @param shortcutType The shortcut type.
* @return The array set of the strings
*/
public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) {
+ return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType));
+ }
+
+ private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) {
if (shortcutType == UserShortcutType.HARDWARE) {
return mAccessibilityShortcutKeyTargets;
} else if (shortcutType == UserShortcutType.SOFTWARE) {
return mAccessibilityButtonTargets;
} else if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
- return getA11yQsTargets();
+ return mAccessibilityQsTargets;
} else if ((shortcutType == UserShortcutType.TRIPLETAP
&& isMagnificationSingleFingerTripleTapEnabledLocked()) || (
shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP
@@ -795,6 +800,32 @@
}
/**
+ * Updates the corresponding shortcut targets with the provided set.
+ * Tap shortcuts don't operate using sets of targets,
+ * so trying to update {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}
+ * will instead throw an {@code IllegalArgumentException}
+ * @param newTargets set of targets to replace the existing set.
+ * @param shortcutType type to be replaced.
+ * @return {@code true} if the set was changed, or {@code false} if the elements are the same.
+ * @throws IllegalArgumentException if {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} is used.
+ */
+ boolean updateShortcutTargetsLocked(
+ Set<String> newTargets, @UserShortcutType int shortcutType) {
+ final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP;
+ if ((shortcutType & mask) != 0) {
+ throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets.");
+ }
+
+ final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType);
+ if (newTargets.equals(currentTargets)) {
+ return false;
+ }
+ currentTargets.clear();
+ currentTargets.addAll(newTargets);
+ return true;
+ }
+
+ /**
* Whether or not the given shortcut target is installed in device.
*
* @param name The shortcut target name
@@ -828,7 +859,7 @@
}
/**
- * Removes given shortcut target in the list.
+ * Removes given shortcut target in the set.
*
* @param shortcutType The shortcut type.
* @param target The component name of the shortcut target.
@@ -844,8 +875,9 @@
);
}
- Set<String> targets = getShortcutTargetsLocked(shortcutType);
- boolean result = targets.removeIf(name -> {
+ // getting internal set lets us directly modify targets, as it's not a copy.
+ Set<String> targets = getShortcutTargetsInternalLocked(shortcutType);
+ return targets.removeIf(name -> {
ComponentName componentName;
if (name == null
|| (componentName = ComponentName.unflattenFromString(name)) == null) {
@@ -853,11 +885,6 @@
}
return componentName.equals(target);
});
- if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
- updateA11yQsTargetLocked(targets);
- }
-
- return result;
}
/**
@@ -1114,11 +1141,6 @@
);
}
- public void updateA11yQsTargetLocked(Set<String> targets) {
- mAccessibilityQsTargets.clear();
- mAccessibilityQsTargets.addAll(targets);
- }
-
/**
* Returns a copy of the targets which has qs shortcut turned on
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 9747579..2945af5 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -34,6 +34,7 @@
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.accessibility.Flags;
import com.android.internal.R;
import com.android.internal.accessibility.util.AccessibilityUtils;
@@ -328,6 +329,18 @@
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER,
InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD);
return true;
+ case AccessibilityService.GLOBAL_ACTION_MENU:
+ if (Flags.globalActionMenu()) {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MENU,
+ InputDevice.SOURCE_KEYBOARD);
+ }
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_MEDIA_PLAY_PAUSE:
+ if (Flags.globalActionMediaPlayPause()) {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
+ InputDevice.SOURCE_KEYBOARD);
+ }
+ return true;
default:
Slog.e(TAG, "Invalid action id: " + actionId);
return false;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5567707..76f6d17 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -17,6 +17,7 @@
package com.android.server.appwidget;
import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
+import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -166,6 +167,8 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
import java.util.function.LongSupplier;
class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
@@ -342,6 +345,11 @@
LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal());
}
+ @Override
+ public int getMaxBitmapMemory() {
+ return mMaxWidgetBitmapMemory;
+ }
+
void systemServicesReady() {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
@@ -457,7 +465,7 @@
break;
case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
added = true;
- // Follow through
+ // fall through
case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
break;
@@ -3571,6 +3579,13 @@
}
out.endTag(null, "gs");
+
+ if (supportResumeRestoreAfterReboot()
+ && mBackupRestoreController.requiresPersistenceLocked()) {
+ AppWidgetXmlUtil.writeBackupRestoreControllerState(
+ out, mBackupRestoreController.getStateLocked(userId));
+ }
+
out.endDocument();
return true;
} catch (IOException e) {
@@ -3717,6 +3732,32 @@
LoadedWidgetState loadedWidgets = new LoadedWidgetState(widget,
hostTag, providerTag);
outLoadedWidgets.add(loadedWidgets);
+ } else if (supportResumeRestoreAfterReboot()
+ && AppWidgetXmlUtil.TAG_BACKUP_RESTORE_CONTROLLER_STATE.equals(tag)) {
+ final BackupRestoreController.State s =
+ AppWidgetXmlUtil.readBackupRestoreControllerState(parser);
+ if (s == null) {
+ continue;
+ }
+ final Set<String> prunedAppsInFile = s.getPrunedApps();
+ if (prunedAppsInFile != null) {
+ final Set<String> prunedAppsInMemory = mBackupRestoreController
+ .mPrunedAppsPerUser.get(userId);
+ if (prunedAppsInMemory == null) {
+ mBackupRestoreController.mPrunedAppsPerUser.put(
+ userId, prunedAppsInFile);
+ } else {
+ prunedAppsInMemory.addAll(prunedAppsInFile);
+ }
+ }
+ loadUpdateRecords(s.getUpdatesByProvider(),
+ this::findProviderByTag,
+ mBackupRestoreController.mUpdatesByProvider::get,
+ mBackupRestoreController.mUpdatesByProvider::put);
+ loadUpdateRecords(s.getUpdatesByHost(),
+ this::findHostByTag,
+ mBackupRestoreController.mUpdatesByHost::get,
+ mBackupRestoreController.mUpdatesByHost::put);
}
}
} while (type != XmlPullParser.END_DOCUMENT);
@@ -3732,6 +3773,36 @@
return version;
}
+ private <T> void loadUpdateRecords(
+ @Nullable final SparseArray<
+ List<BackupRestoreController.RestoreUpdateRecord>> updatesOnFile,
+ @NonNull final Function<Integer, T> findKeyByTagCb,
+ @NonNull final Function<T, List<
+ BackupRestoreController.RestoreUpdateRecord>> findRecordsCb,
+ @NonNull final BiConsumer<T, List<
+ BackupRestoreController.RestoreUpdateRecord>> newRecordsCb) {
+ if (updatesOnFile == null) {
+ return;
+ }
+ for (int i = 0; i < updatesOnFile.size(); i++) {
+ final int tag = updatesOnFile.keyAt(i);
+ final List<
+ BackupRestoreController.RestoreUpdateRecord
+ > recordsOnFile = updatesOnFile.get(tag);
+ if (recordsOnFile == null || recordsOnFile.isEmpty()) {
+ continue;
+ }
+ final T key = findKeyByTagCb.apply(tag);
+ final List<BackupRestoreController.RestoreUpdateRecord> recordsInMemory =
+ findRecordsCb.apply(key);
+ if (recordsInMemory != null) {
+ recordsInMemory.addAll(recordsOnFile);
+ } else {
+ newRecordsCb.accept(key, recordsOnFile);
+ }
+ }
+ }
+
private void performUpgradeLocked(int fromVersion) {
if (fromVersion < CURRENT_VERSION) {
Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to "
@@ -4674,7 +4745,7 @@
}
}
- private static final class Provider {
+ static final class Provider {
ProviderId id;
AppWidgetProviderInfo info;
@@ -4931,7 +5002,7 @@
}
}
- private static final class Host {
+ static final class Host {
HostId id;
ArrayList<Widget> widgets = new ArrayList<>();
IAppWidgetHost callbacks;
@@ -5250,10 +5321,10 @@
/**
* This class encapsulates the backup and restore logic for a user group state.
*/
- private final class BackupRestoreController {
+ final class BackupRestoreController {
private static final String TAG = "BackupRestoreController";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = AppWidgetServiceImpl.DEBUG;
// Version of backed-up widget state.
private static final int WIDGET_STATE_VERSION = 2;
@@ -5262,16 +5333,31 @@
// a given package. Keep track of what we've done so far here; the list is
// cleared at the start of every system restore pass, but preserved through
// any install-time restore operations.
+ @GuardedBy("AppWidgetServiceImpl.this.mLock")
private final SparseArray<Set<String>> mPrunedAppsPerUser = new SparseArray<>();
- private final HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider =
- new HashMap<>();
- private final HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost =
- new HashMap<>();
+ @GuardedBy("AppWidgetServiceImpl.this.mLock")
+ final Map<Provider, List<RestoreUpdateRecord>> mUpdatesByProvider =
+ new ArrayMap<>();
- @GuardedBy("mLock")
+ @GuardedBy("AppWidgetServiceImpl.this.mLock")
+ private final Map<Host, List<RestoreUpdateRecord>> mUpdatesByHost =
+ new ArrayMap<>();
+
+ @GuardedBy("AppWidgetServiceImpl.this.mLock")
private boolean mHasSystemRestoreFinished;
+ @GuardedBy("AppWidgetServiceImpl.this.mLock")
+ public boolean requiresPersistenceLocked() {
+ if (mHasSystemRestoreFinished) {
+ // No need to persist intermediate states if system restore is already finished.
+ return false;
+ }
+ // If either of the internal states is non-empty, then we need to persist that
+ return !(mPrunedAppsPerUser.size() == 0 && mUpdatesByProvider.isEmpty()
+ && mUpdatesByHost.isEmpty());
+ }
+
public List<String> getWidgetParticipants(int userId) {
if (DEBUG) {
Slog.i(TAG, "Getting widget participants for user: " + userId);
@@ -5436,7 +5522,7 @@
// If there's no live entry for this provider, add an inactive one
// so that widget IDs referring to them can be properly allocated
- // Backup and resotre only for the parent profile.
+ // Backup and restore only for the parent profile.
ComponentName componentName = new ComponentName(pkg, cl);
Provider p = findProviderLocked(componentName, userId);
@@ -5579,9 +5665,9 @@
final UserHandle userHandle = new UserHandle(userId);
// Build the providers' broadcasts and send them off
- Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+ Set<Map.Entry<Provider, List<RestoreUpdateRecord>>> providerEntries
= mUpdatesByProvider.entrySet();
- for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+ for (Map.Entry<Provider, List<RestoreUpdateRecord>> e : providerEntries) {
// For each provider there's a list of affected IDs
Provider provider = e.getKey();
if (provider.zombie) {
@@ -5589,7 +5675,7 @@
// We'll be called again when the provider is installed.
continue;
}
- ArrayList<RestoreUpdateRecord> updates = e.getValue();
+ List<RestoreUpdateRecord> updates = e.getValue();
final int pending = countPendingUpdates(updates);
if (DEBUG) {
Slog.i(TAG, "Provider " + provider + " pending: " + pending);
@@ -5618,12 +5704,12 @@
}
// same thing per host
- Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+ Set<Map.Entry<Host, List<RestoreUpdateRecord>>> hostEntries
= mUpdatesByHost.entrySet();
- for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+ for (Map.Entry<Host, List<RestoreUpdateRecord>> e : hostEntries) {
Host host = e.getKey();
if (host.id.uid != UNKNOWN_UID) {
- ArrayList<RestoreUpdateRecord> updates = e.getValue();
+ List<RestoreUpdateRecord> updates = e.getValue();
final int pending = countPendingUpdates(updates);
if (DEBUG) {
Slog.i(TAG, "Host " + host + " pending: " + pending);
@@ -5714,8 +5800,9 @@
return false;
}
+ @GuardedBy("mLock")
private void stashProviderRestoreUpdateLocked(Provider provider, int oldId, int newId) {
- ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
+ List<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
if (r == null) {
r = new ArrayList<>();
mUpdatesByProvider.put(provider, r);
@@ -5732,7 +5819,7 @@
r.add(new RestoreUpdateRecord(oldId, newId));
}
- private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash,
+ private boolean alreadyStashed(List<RestoreUpdateRecord> stash,
final int oldId, final int newId) {
final int N = stash.size();
for (int i = 0; i < N; i++) {
@@ -5744,8 +5831,9 @@
return false;
}
+ @GuardedBy("mLock")
private void stashHostRestoreUpdateLocked(Host host, int oldId, int newId) {
- ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
+ List<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
if (r == null) {
r = new ArrayList<>();
mUpdatesByHost.put(host, r);
@@ -5835,7 +5923,7 @@
|| widget.provider.getUserId() == userId);
}
- private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
+ private int countPendingUpdates(List<RestoreUpdateRecord> updates) {
int pending = 0;
final int N = updates.size();
for (int i = 0; i < N; i++) {
@@ -5847,9 +5935,28 @@
return pending;
}
+ @GuardedBy("mLock")
+ @NonNull
+ private State getStateLocked(final int userId) {
+ final Set<String> prunedApps = mPrunedAppsPerUser.get(userId);
+ final SparseArray<List<RestoreUpdateRecord>> updatesByProvider = new SparseArray<>();
+ final SparseArray<List<RestoreUpdateRecord>> updatesByHost = new SparseArray<>();
+ mUpdatesByProvider.forEach((p, updates) -> {
+ if (p.getUserId() == userId) {
+ updatesByProvider.put(p.tag, new ArrayList<>(updates));
+ }
+ });
+ mUpdatesByHost.forEach((h, updates) -> {
+ if (h.getUserId() == userId) {
+ updatesByHost.put(h.tag, new ArrayList<>(updates));
+ }
+ });
+ return new State(prunedApps, updatesByProvider, updatesByHost);
+ }
+
// Accumulate a list of updates that affect the given provider for a final
// coalesced notification broadcast once restore is over.
- private class RestoreUpdateRecord {
+ static class RestoreUpdateRecord {
public int oldId;
public int newId;
public boolean notified;
@@ -5860,6 +5967,45 @@
notified = false;
}
}
+
+ static final class State {
+ // We need to make sure to wipe the pre-restore widget state only once for
+ // a given package. Keep track of what we've done so far here; the list is
+ // cleared at the start of every system restore pass, but preserved through
+ // any install-time restore operations.
+ @Nullable
+ private final Set<String> mPrunedApps;
+
+ @Nullable
+ private final SparseArray<List<RestoreUpdateRecord>> mUpdatesByProvider;
+
+ @Nullable
+ private final SparseArray<List<RestoreUpdateRecord>> mUpdatesByHost;
+
+ State(
+ @Nullable final Set<String> prunedApps,
+ @Nullable final SparseArray<List<RestoreUpdateRecord>> updatesByProvider,
+ @Nullable final SparseArray<List<RestoreUpdateRecord>> updatesByHost) {
+ mPrunedApps = prunedApps;
+ mUpdatesByProvider = updatesByProvider;
+ mUpdatesByHost = updatesByHost;
+ }
+
+ @Nullable
+ Set<String> getPrunedApps() {
+ return mPrunedApps;
+ }
+
+ @Nullable
+ SparseArray<List<BackupRestoreController.RestoreUpdateRecord>> getUpdatesByProvider() {
+ return mUpdatesByProvider;
+ }
+
+ @Nullable
+ SparseArray<List<BackupRestoreController.RestoreUpdateRecord>> getUpdatesByHost() {
+ return mUpdatesByHost;
+ }
+ }
}
private class AppWidgetManagerLocal extends AppWidgetManagerInternal {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java
index d781cd8..ce9130a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java
@@ -22,17 +22,24 @@
import android.content.ComponentName;
import android.os.Build;
import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
import android.util.SizeF;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -65,6 +72,16 @@
private static final String ATTR_DESCRIPTION_RES = "description_res";
private static final String ATTR_PROVIDER_INHERITANCE = "provider_inheritance";
private static final String ATTR_OS_FINGERPRINT = "os_fingerprint";
+ static final String TAG_BACKUP_RESTORE_CONTROLLER_STATE = "br";
+ private static final String TAG_PRUNED_APPS = "pruned_apps";
+ private static final String ATTR_TAG = "tag";
+ private static final String ATTR_PACKAGE_NAMES = "pkgs";
+ private static final String TAG_PROVIDER_UPDATES = "provider_updates";
+ private static final String TAG_HOST_UPDATES = "host_updates";
+ private static final String TAG_RECORD = "record";
+ private static final String ATTR_OLD_ID = "old_id";
+ private static final String ATTR_NEW_ID = "new_id";
+ private static final String ATTR_NOTIFIED = "notified";
private static final String SIZE_SEPARATOR = ",";
/**
@@ -165,4 +182,168 @@
return null;
}
}
+
+ /**
+ * Persists {@link AppWidgetServiceImpl.BackupRestoreController.State} to disk as XML.
+ * See {@link #readBackupRestoreControllerState(TypedXmlPullParser)} for example XML.
+ *
+ * @param out XML serializer
+ * @param state {@link AppWidgetServiceImpl.BackupRestoreController.State} of
+ * intermediate states to be persisted as xml to resume restore after reboot.
+ */
+ static void writeBackupRestoreControllerState(
+ @NonNull final TypedXmlSerializer out,
+ @NonNull final AppWidgetServiceImpl.BackupRestoreController.State state)
+ throws IOException {
+ Objects.requireNonNull(out);
+ Objects.requireNonNull(state);
+ out.startTag(null, TAG_BACKUP_RESTORE_CONTROLLER_STATE);
+ final Set<String> prunedApps = state.getPrunedApps();
+ if (prunedApps != null && !prunedApps.isEmpty()) {
+ out.startTag(null, TAG_PRUNED_APPS);
+ out.attribute(null, ATTR_PACKAGE_NAMES, String.join(",", prunedApps));
+ out.endTag(null, TAG_PRUNED_APPS);
+ }
+ final SparseArray<List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>>
+ updatesByProvider = state.getUpdatesByProvider();
+ if (updatesByProvider != null) {
+ writeUpdateRecords(out, TAG_PROVIDER_UPDATES, updatesByProvider);
+ }
+ final SparseArray<List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>>
+ updatesByHost = state.getUpdatesByHost();
+ if (updatesByHost != null) {
+ writeUpdateRecords(out, TAG_HOST_UPDATES, updatesByHost);
+ }
+ out.endTag(null, TAG_BACKUP_RESTORE_CONTROLLER_STATE);
+ }
+
+ private static void writeUpdateRecords(@NonNull final TypedXmlSerializer out,
+ @NonNull final String outerTag, @NonNull final SparseArray<List<
+ AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>> records)
+ throws IOException {
+ for (int i = 0; i < records.size(); i++) {
+ final int tag = records.keyAt(i);
+ out.startTag(null, outerTag);
+ out.attributeInt(null, ATTR_TAG, tag);
+ final List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord> entries =
+ records.get(tag);
+ for (AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord entry : entries) {
+ out.startTag(null, TAG_RECORD);
+ out.attributeInt(null, ATTR_OLD_ID, entry.oldId);
+ out.attributeInt(null, ATTR_NEW_ID, entry.newId);
+ out.attributeBoolean(null, ATTR_NOTIFIED, entry.notified);
+ out.endTag(null, TAG_RECORD);
+ }
+ out.endTag(null, outerTag);
+ }
+ }
+
+ /**
+ * Parses {@link AppWidgetServiceImpl.BackupRestoreController.State} from xml.
+ *
+ * <pre>
+ * {@code
+ * <?xml version="1.0"?>
+ * <br>
+ * <pruned_apps pkgs="com.example.app1,com.example.app2,com.example.app3" />
+ * <provider_updates tag="0">
+ * <record old_id="10" new_id="0" notified="false" />
+ * </provider_updates>
+ * <provider_updates tag="1">
+ * <record old_id="9" new_id="1" notified="true" />
+ * </provider_updates>
+ * <provider_updates tag="2">
+ * <record old_id="8" new_id="2" notified="false" />
+ * </provider_updates>
+ * <host_updates tag="0">
+ * <record old_id="10" new_id="0" notified="false" />
+ * </host_updates>
+ * <host_updates tag="1">
+ * <record old_id="9" new_id="1" notified="true" />
+ * </host_updates>
+ * <host_updates tag="2">
+ * <record old_id="8" new_id="2" notified="false" />
+ * </host_updates>
+ * </br>
+ * }
+ * </pre>
+ *
+ * @param parser XML parser
+ * @return {@link AppWidgetServiceImpl.BackupRestoreController.State} of intermediate states
+ * in {@link AppWidgetServiceImpl.BackupRestoreController}, so that backup & restore can be
+ * resumed after reboot.
+ */
+ @Nullable
+ static AppWidgetServiceImpl.BackupRestoreController.State
+ readBackupRestoreControllerState(@NonNull final TypedXmlPullParser parser) {
+ Objects.requireNonNull(parser);
+ int type;
+ String tag = null;
+ final Set<String> prunedApps = new ArraySet<>(1);
+ final SparseArray<List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>>
+ updatesByProviders = new SparseArray<>();
+ final SparseArray<List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>>
+ updatesByHosts = new SparseArray<>();
+
+ try {
+ do {
+ type = parser.next();
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ tag = parser.getName();
+ switch (tag) {
+ case TAG_PRUNED_APPS:
+ final String packages =
+ parser.getAttributeValue(null, ATTR_PACKAGE_NAMES);
+ prunedApps.addAll(Arrays.asList(packages.split(",")));
+ break;
+ case TAG_PROVIDER_UPDATES:
+ updatesByProviders.put(parser.getAttributeInt(null, ATTR_TAG),
+ parseRestoreUpdateRecords(parser));
+ break;
+ case TAG_HOST_UPDATES:
+ updatesByHosts.put(parser.getAttributeInt(null, ATTR_TAG),
+ parseRestoreUpdateRecords(parser));
+ break;
+ default:
+ break;
+ }
+ } while (type != XmlPullParser.END_DOCUMENT
+ && (!TAG_BACKUP_RESTORE_CONTROLLER_STATE.equals(tag)
+ || type != XmlPullParser.END_TAG));
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "error parsing state", e);
+ return null;
+ }
+ return new AppWidgetServiceImpl.BackupRestoreController.State(
+ prunedApps, updatesByProviders, updatesByHosts);
+ }
+
+ @NonNull
+ private static List<
+ AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord
+ > parseRestoreUpdateRecords(@NonNull final TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ String tag;
+ final List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord> ret =
+ new ArrayList<>();
+ do {
+ type = parser.next();
+ tag = parser.getName();
+ if (tag.equals(TAG_RECORD) && type == XmlPullParser.START_TAG) {
+ final int oldId = parser.getAttributeInt(null, ATTR_OLD_ID);
+ final int newId = parser.getAttributeInt(null, ATTR_NEW_ID);
+ final boolean notified = parser.getAttributeBoolean(
+ null, ATTR_NOTIFIED);
+ final AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord record =
+ new AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord(
+ oldId, newId);
+ record.notified = notified;
+ ret.add(record);
+ }
+ } while (tag.equals(TAG_RECORD));
+ return ret;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index 3fbd856..b3a2da4 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -19,7 +19,6 @@
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
-import static android.companion.CompanionDeviceManager.REASON_INTERNAL_ERROR;
import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR;
import static android.content.ComponentName.createRelative;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -183,7 +182,7 @@
String errorMessage = "3p apps are not allowed to create associations on watch.";
Slog.e(TAG, errorMessage);
try {
- callback.onFailure(errorMessage);
+ callback.onFailure(RESULT_INTERNAL_ERROR);
} catch (RemoteException e) {
// ignored
}
@@ -252,8 +251,9 @@
} catch (SecurityException e) {
// Since, at this point the caller is our own UI, we need to catch the exception on
// forward it back to the application via the callback.
+ Slog.e(TAG, e.getMessage());
try {
- callback.onFailure(e.getMessage());
+ callback.onFailure(RESULT_INTERNAL_ERROR);
} catch (RemoteException ignore) {
}
return;
@@ -378,7 +378,7 @@
// Send the association back via the app's callback
if (callback != null) {
try {
- callback.onFailure(REASON_INTERNAL_ERROR);
+ callback.onFailure(RESULT_INTERNAL_ERROR);
} catch (RemoteException ignore) {
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
index ce4067c..ef39846 100644
--- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
+++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
@@ -192,7 +192,8 @@
for (UserInfo user : aliveUsers) {
int userId = user.getUserHandle().getIdentifier();
int appUid = queryUidFromPackageName(userId, packageName);
- if (mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(appUid)) {
+ if (mVirtualDeviceManagerInternal != null
+ && mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(appUid)) {
if (data == null) {
data = new InjectionSessionData();
data.appUid = appUid;
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 70af49c..d091ce8 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -31,6 +31,7 @@
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
+import android.hardware.input.VirtualRotaryEncoderScrollEvent;
import android.hardware.input.VirtualStylusButtonEvent;
import android.hardware.input.VirtualStylusMotionEvent;
import android.hardware.input.VirtualTouchEvent;
@@ -76,6 +77,7 @@
static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen";
static final String PHYS_TYPE_NAVIGATION_TOUCHPAD = "NavigationTouchpad";
static final String PHYS_TYPE_STYLUS = "Stylus";
+ static final String PHYS_TYPE_ROTARY_ENCODER = "RotaryEncoder";
@StringDef(prefix = { "PHYS_TYPE_" }, value = {
PHYS_TYPE_DPAD,
PHYS_TYPE_KEYBOARD,
@@ -83,6 +85,7 @@
PHYS_TYPE_TOUCHSCREEN,
PHYS_TYPE_NAVIGATION_TOUCHPAD,
PHYS_TYPE_STYLUS,
+ PHYS_TYPE_ROTARY_ENCODER,
})
@Retention(RetentionPolicy.SOURCE)
@interface PhysType {
@@ -206,6 +209,15 @@
height, width));
}
+ void createRotaryEncoder(@NonNull String deviceName, int vendorId, int productId,
+ @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException {
+ final String phys = createPhys(PHYS_TYPE_ROTARY_ENCODER);
+ createDeviceInternal(InputDeviceDescriptor.TYPE_ROTARY_ENCODER, deviceName, vendorId,
+ productId, deviceToken, displayId, phys,
+ () -> mNativeWrapper.openUinputRotaryEncoder(deviceName, vendorId, productId,
+ phys));
+ }
+
void unregisterInputDevice(@NonNull IBinder token) {
synchronized (mLock) {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove(
@@ -403,6 +415,20 @@
}
}
+ boolean sendRotaryEncoderScrollEvent(@NonNull IBinder token,
+ @NonNull VirtualRotaryEncoderScrollEvent event) {
+ synchronized (mLock) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
+ return false;
+ }
+ return mNativeWrapper.writeRotaryEncoderScrollEvent(
+ inputDeviceDescriptor.getNativePointer(), event.getScrollAmount(),
+ event.getEventTimeNanos());
+ }
+ }
+
public void dump(@NonNull PrintWriter fout) {
fout.println(" InputController: ");
synchronized (mLock) {
@@ -449,6 +475,9 @@
int productId, String phys, int height, int width);
private static native long nativeOpenUinputStylus(String deviceName, int vendorId,
int productId, String phys, int height, int width);
+ private static native long nativeOpenUinputRotaryEncoder(String deviceName, int vendorId,
+ int productId, String phys);
+
private static native void nativeCloseUinput(long ptr);
private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action,
long eventTimeNanos);
@@ -467,6 +496,8 @@
int locationX, int locationY, int pressure, int tiltX, int tiltY, long eventTimeNanos);
private static native boolean nativeWriteStylusButtonEvent(long ptr, int buttonCode, int action,
long eventTimeNanos);
+ private static native boolean nativeWriteRotaryEncoderScrollEvent(long ptr, float scrollAmount,
+ long eventTimeNanos);
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
@@ -495,6 +526,11 @@
return nativeOpenUinputStylus(deviceName, vendorId, productId, phys, height, width);
}
+ public long openUinputRotaryEncoder(String deviceName, int vendorId, int productId,
+ String phys) {
+ return nativeOpenUinputRotaryEncoder(deviceName, vendorId, productId, phys);
+ }
+
public void closeUinput(long ptr) {
nativeCloseUinput(ptr);
}
@@ -542,6 +578,11 @@
long eventTimeNanos) {
return nativeWriteStylusButtonEvent(ptr, buttonCode, action, eventTimeNanos);
}
+
+ public boolean writeRotaryEncoderScrollEvent(long ptr, float scrollAmount,
+ long eventTimeNanos) {
+ return nativeWriteRotaryEncoderScrollEvent(ptr, scrollAmount, eventTimeNanos);
+ }
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -553,6 +594,7 @@
static final int TYPE_DPAD = 4;
static final int TYPE_NAVIGATION_TOUCHPAD = 5;
static final int TYPE_STYLUS = 6;
+ static final int TYPE_ROTARY_ENCODER = 7;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_KEYBOARD,
TYPE_MOUSE,
@@ -560,6 +602,7 @@
TYPE_DPAD,
TYPE_NAVIGATION_TOUCHPAD,
TYPE_STYLUS,
+ TYPE_ROTARY_ENCODER,
})
@Retention(RetentionPolicy.SOURCE)
@interface Type {
@@ -821,6 +864,8 @@
return "virtual_devices.value_virtual_navigationtouchpad_created_count";
case InputDeviceDescriptor.TYPE_STYLUS:
return "virtual_devices.value_virtual_stylus_created_count";
+ case InputDeviceDescriptor.TYPE_ROTARY_ENCODER:
+ return "virtual_devices.value_virtual_rotary_created_count";
default:
Log.e(TAG, "No metric known for input type: " + type);
return null;
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index cf48180..0655685 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -229,6 +229,10 @@
Slog.e(TAG, "No sensor callback configured for sensor handle " + handle);
return BAD_VALUE;
}
+ if (mVdmInternal == null) {
+ Slog.e(TAG, "Virtual Device Manager is not enabled.");
+ return BAD_VALUE;
+ }
VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, handle);
if (sensor == null) {
Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId
@@ -285,6 +289,10 @@
Slog.e(TAG, "No runtime sensor callback configured.");
return BAD_VALUE;
}
+ if (mVdmInternal == null) {
+ Slog.e(TAG, "Virtual Device Manager is not enabled.");
+ return BAD_VALUE;
+ }
VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, sensorHandle);
if (sensor == null) {
Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 6704049..a72259e 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -77,6 +77,8 @@
import android.hardware.input.VirtualMouseRelativeEvent;
import android.hardware.input.VirtualMouseScrollEvent;
import android.hardware.input.VirtualNavigationTouchpadConfig;
+import android.hardware.input.VirtualRotaryEncoderConfig;
+import android.hardware.input.VirtualRotaryEncoderScrollEvent;
import android.hardware.input.VirtualStylusButtonEvent;
import android.hardware.input.VirtualStylusConfig;
import android.hardware.input.VirtualStylusMotionEvent;
@@ -183,8 +185,8 @@
private final SparseIntArray mDevicePolicies;
@GuardedBy("mVirtualDeviceLock")
private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>();
- private final IVirtualDeviceActivityListener mActivityListener;
- private final IVirtualDeviceSoundEffectListener mSoundEffectListener;
+ private IVirtualDeviceActivityListener mActivityListener;
+ private IVirtualDeviceSoundEffectListener mSoundEffectListener;
private final DisplayManagerGlobal mDisplayManager;
private final DisplayManagerInternal mDisplayManagerInternal;
@GuardedBy("mVirtualDeviceLock")
@@ -301,7 +303,9 @@
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
- mPersistentDeviceId = createPersistentDeviceId(associationInfo.getId());
+ mPersistentDeviceId = associationInfo == null
+ ? null
+ : createPersistentDeviceId(associationInfo.getId());
mService = service;
mPendingTrampolineCallback = pendingTrampolineCallback;
mActivityListener = activityListener;
@@ -403,7 +407,7 @@
/** Returns the device display name. */
CharSequence getDisplayName() {
- return mAssociationInfo.getDisplayName();
+ return mAssociationInfo == null ? mParams.getName() : mAssociationInfo.getDisplayName();
}
/** Returns the public representation of the device. */
@@ -418,6 +422,22 @@
}
}
+ /**
+ * Setter for listeners that live in the client process, namely in
+ * {@link android.companion.virtual.VirtualDeviceInternal}.
+ *
+ * This is needed for virtual devices that are created by the system, as the VirtualDeviceImpl
+ * object is created before the returned VirtualDeviceInternal one.
+ */
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void setListeners(@NonNull IVirtualDeviceActivityListener activityListener,
+ @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) {
+ super.setListeners_enforcePermission();
+ mActivityListener = Objects.requireNonNull(activityListener);
+ mSoundEffectListener = Objects.requireNonNull(soundEffectListener);
+ }
+
@Override // Binder call
public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
@VirtualDeviceParams.PolicyType int policyType) {
@@ -454,7 +474,9 @@
@Override // Binder call
public int getAssociationId() {
- return mAssociationInfo.getId();
+ return mAssociationInfo == null
+ ? VirtualDeviceManagerService.CDM_ASSOCIATION_ID_NONE
+ : mAssociationInfo.getId();
}
@Override // Binder call
@@ -812,6 +834,26 @@
@Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void createVirtualRotaryEncoder(@NonNull VirtualRotaryEncoderConfig config,
+ @NonNull IBinder deviceToken) {
+ super.createVirtualRotaryEncoder_enforcePermission();
+ Objects.requireNonNull(config);
+ Objects.requireNonNull(deviceToken);
+ checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId());
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mInputController.createRotaryEncoder(config.getInputDeviceName(), config.getVendorId(),
+ config.getProductId(), deviceToken,
+ getTargetDisplayIdForInput(config.getAssociatedDisplayId()));
+ } catch (InputController.DeviceCreationException e) {
+ throw new IllegalArgumentException(e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void unregisterInputDevice(IBinder token) {
super.unregisterInputDevice_enforcePermission();
final long ident = Binder.clearCallingIdentity();
@@ -947,6 +989,19 @@
@Override // Binder call
@EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public boolean sendRotaryEncoderScrollEvent(@NonNull IBinder token,
+ @NonNull VirtualRotaryEncoderScrollEvent event) {
+ super.sendRotaryEncoderScrollEvent_enforcePermission();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendRotaryEncoderScrollEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void setShowPointerIcon(boolean showPointerIcon) {
super.setShowPointerIcon_enforcePermission();
final long ident = Binder.clearCallingIdentity();
@@ -1105,7 +1160,7 @@
String indent = " ";
fout.println(" VirtualDevice: ");
fout.println(indent + "mDeviceId: " + mDeviceId);
- fout.println(indent + "mAssociationId: " + mAssociationInfo.getId());
+ fout.println(indent + "mAssociationId: " + getAssociationId());
fout.println(indent + "mOwnerPackageName: " + mOwnerPackageName);
fout.println(indent + "mParams: ");
mParams.dump(fout, indent + indent);
@@ -1251,8 +1306,7 @@
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
private void onActivityBlocked(int displayId, ActivityInfo activityInfo) {
- Intent intent = BlockedAppStreamingActivity.createIntent(
- activityInfo, mAssociationInfo.getDisplayName());
+ Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
mContext.startActivityAsUser(
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
@@ -1339,7 +1393,7 @@
@SuppressWarnings("AndroidFrameworkRequiresPermission")
private void checkVirtualInputDeviceDisplayIdAssociation(int displayId) {
- if (mContext.checkCallingPermission(android.Manifest.permission.INJECT_EVENTS)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INJECT_EVENTS)
== PackageManager.PERMISSION_GRANTED) {
// The INJECT_EVENTS permission allows for injecting input to any window / display.
return;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
index c65aa5b..b0bacfd 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.Process;
import android.util.SparseArray;
import java.io.PrintWriter;
@@ -35,6 +36,8 @@
"MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
private static final int MAX_ENTRIES = 16;
+ private static final String VIRTUAL_DEVICE_OWNER_SYSTEM = "system";
+
private final Context mContext;
private final ArrayDeque<LogEntry> mLogEntries = new ArrayDeque<>();
@@ -132,6 +135,8 @@
String[] packages;
if (mUidToPackagesCache.contains(ownerUid)) {
return mUidToPackagesCache.get(ownerUid);
+ } else if (ownerUid == Process.SYSTEM_UID) {
+ return VIRTUAL_DEVICE_OWNER_SYSTEM;
} else {
packages = mPackageManager.getPackagesForUid(ownerUid);
String packageName = "";
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 9ad73ca..1be1d2b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -101,6 +101,11 @@
AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING);
+ /**
+ * A virtual device association id corresponding to no CDM association.
+ */
+ static final int CDM_ASSOCIATION_ID_NONE = 0;
+
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
private final VirtualDeviceManagerNativeImpl mNativeImpl;
@@ -316,7 +321,9 @@
for (int i = 0; i < mVirtualDevices.size(); i++) {
VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
- if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) {
+ int deviceAssociationId = virtualDevice.getAssociationId();
+ if (deviceAssociationId != CDM_ASSOCIATION_ID_NONE
+ && !activeAssociationIds.contains(deviceAssociationId)) {
virtualDevicesToRemove.add(virtualDevice);
}
}
@@ -422,28 +429,39 @@
@NonNull IVirtualDeviceActivityListener activityListener,
@NonNull IVirtualDeviceSoundEffectListener soundEffectListener) {
createVirtualDevice_enforcePermission();
- attributionSource.enforceCallingUid();
-
- final int callingUid = getCallingUid();
+ Objects.requireNonNull(activityListener);
+ Objects.requireNonNull(soundEffectListener);
final String packageName = attributionSource.getPackageName();
- if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
- throw new SecurityException(
- "Package name " + packageName + " does not belong to calling uid "
- + callingUid);
- }
AssociationInfo associationInfo = getAssociationInfo(packageName, associationId);
if (associationInfo == null) {
throw new IllegalArgumentException("No association with ID " + associationId);
- }
- if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
+ } else if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES
.contains(associationInfo.getDeviceProfile())
&& Flags.persistentDeviceIdApi()) {
throw new IllegalArgumentException("Unsupported CDM Association device profile "
+ associationInfo.getDeviceProfile() + " for virtual device creation.");
}
+ return createVirtualDevice(token, attributionSource, associationInfo, params,
+ activityListener, soundEffectListener);
+ }
+
+ private IVirtualDevice createVirtualDevice(
+ IBinder token,
+ AttributionSource attributionSource,
+ AssociationInfo associationInfo,
+ @NonNull VirtualDeviceParams params,
+ @Nullable IVirtualDeviceActivityListener activityListener,
+ @Nullable IVirtualDeviceSoundEffectListener soundEffectListener) {
+ createVirtualDevice_enforcePermission();
+ attributionSource.enforceCallingUid();
+
+ final String packageName = attributionSource.getPackageName();
+ if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
+ throw new SecurityException(
+ "Package name " + packageName + " does not belong to calling uid "
+ + getCallingUid());
+ }
Objects.requireNonNull(params);
- Objects.requireNonNull(activityListener);
- Objects.requireNonNull(soundEffectListener);
final UserHandle userHandle = getCallingUserHandle();
final CameraAccessController cameraAccessController =
@@ -724,6 +742,21 @@
private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>();
@Override
+ public @NonNull VirtualDeviceManager.VirtualDevice createVirtualDevice(
+ @NonNull VirtualDeviceParams params) {
+ Objects.requireNonNull(params, "params must not be null");
+ Objects.requireNonNull(params.getName(), "virtual device name must not be null");
+ IVirtualDevice virtualDevice = mImpl.createVirtualDevice(
+ new Binder(),
+ getContext().getAttributionSource(),
+ /* associationInfo= */ null,
+ params,
+ /* activityListener= */ null,
+ /* soundEffectListener= */ null);
+ return new VirtualDeviceManager.VirtualDevice(mImpl, getContext(), virtualDevice);
+ }
+
+ @Override
public int getDeviceOwnerUid(int deviceId) {
VirtualDeviceImpl virtualDevice;
synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index b64aa8a..ea6351b 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -364,7 +364,7 @@
}
@RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS)
- private int invokeContextualSearchIntent(Intent launchIntent) {
+ private int invokeContextualSearchIntent(Intent launchIntent, final int userId) {
// Contextual search starts with a frozen screen - so we launch without
// any system animations or starting window.
final ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(mContext,
@@ -372,7 +372,7 @@
opts.setDisableStartingWindow(true);
return mAtmInternal.startActivityWithScreenshot(launchIntent,
mContext.getPackageName(), Binder.getCallingUid(), Binder.getCallingPid(), null,
- opts.toBundle(), Binder.getCallingUserHandle().getIdentifier());
+ opts.toBundle(), userId);
}
private void enforcePermission(@NonNull final String func) {
@@ -446,6 +446,8 @@
synchronized (this) {
if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
enforcePermission("startContextualSearch");
+ final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+
mAssistDataRequester.cancel();
// Creates a new CallbackToken at mToken and an expiration handler.
issueToken();
@@ -455,7 +457,7 @@
Binder.withCleanCallingIdentity(() -> {
Intent launchIntent = getContextualSearchIntent(entrypoint, mToken);
if (launchIntent != null) {
- int result = invokeContextualSearchIntent(launchIntent);
+ int result = invokeContextualSearchIntent(launchIntent, callingUserId);
if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
}
});
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 53730e3..d9962c7 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -239,6 +239,9 @@
],
lint: {
baseline_filename: "lint-baseline.xml",
+ warning_checks: [
+ "FlaggedApi",
+ ],
},
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 256f2b3..361b818 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -63,7 +63,7 @@
# NotificationManagerService.java
# ---------------------------
# when a NotificationManager.notify is called. status: 0=post, 1=update, 2=ignored
-2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3),(status|1)
+2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3),(status|1),(app_provided|1)
# when someone tries to cancel a notification, the notification manager sometimes
# calls this with flags too
2751 notification_cancel (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(required_flags|1),(forbidden_flags|1),(reason|1|5),(listener|3)
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 189b2495..dfd148d 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -58,7 +58,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -493,14 +492,6 @@
private static void executeRescueLevelInternalOld(Context context, int level, @Nullable
String failedPackage) throws Exception {
-
- // Note: DeviceConfig reset is disabled currently and would be enabled using the flag,
- // after we have figured out a way to reset flags without interfering with trunk
- // development. TODO: b/287618292 For enabling flag resets.
- if (!Flags.allowRescuePartyFlagResets() && level <= LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS) {
- return;
- }
-
CrashRecoveryStatsLog.write(CrashRecoveryStatsLog.RESCUE_PARTY_RESET_REPORTED,
level, levelToString(level));
// Try our best to reset all settings possible, and once finished
@@ -508,43 +499,10 @@
Exception res = null;
switch (level) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
- try {
- resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
- level);
- } catch (Exception e) {
- res = e;
- }
- try {
- resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
- } catch (Exception e) {
- res = e;
- }
break;
case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
- try {
- resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES,
- level);
- } catch (Exception e) {
- res = e;
- }
- try {
- resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
- } catch (Exception e) {
- res = e;
- }
break;
case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
- try {
- resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS,
- level);
- } catch (Exception e) {
- res = e;
- }
- try {
- resetDeviceConfig(context, /*isScoped=*/false, failedPackage);
- } catch (Exception e) {
- res = e;
- }
break;
case LEVEL_WARM_REBOOT:
executeWarmReboot(context, level, failedPackage);
@@ -572,16 +530,8 @@
level, levelToString(level));
switch (level) {
case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET:
- // Enable deviceConfig reset behind flag
- if (Flags.allowRescuePartyFlagResets()) {
- resetDeviceConfig(context, /*isScoped=*/true, failedPackage);
- }
break;
case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET:
- // Enable deviceConfig reset behind flag
- if (Flags.allowRescuePartyFlagResets()) {
- resetDeviceConfig(context, /*isScoped=*/false, failedPackage);
- }
break;
case RESCUE_LEVEL_WARM_REBOOT:
executeWarmReboot(context, level, failedPackage);
@@ -732,61 +682,6 @@
}
}
- private static void resetDeviceConfig(Context context, boolean isScoped,
- @Nullable String failedPackage) throws Exception {
- final ContentResolver resolver = context.getContentResolver();
- try {
- if (!isScoped || failedPackage == null) {
- resetAllAffectedNamespaces(context);
- } else {
- performScopedReset(context, failedPackage);
- }
- } catch (Exception e) {
- throw new RuntimeException("Failed to reset config settings", e);
- }
- }
-
- private static void resetAllAffectedNamespaces(Context context) {
- RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
- Set<String> allAffectedNamespaces = rescuePartyObserver.getAllAffectedNamespaceSet();
-
- Slog.w(TAG,
- "Performing reset for all affected namespaces: "
- + Arrays.toString(allAffectedNamespaces.toArray()));
- Iterator<String> it = allAffectedNamespaces.iterator();
- while (it.hasNext()) {
- String namespace = it.next();
- // Don't let RescueParty reset the namespace for RescueParty switches.
- if (NAMESPACE_CONFIGURATION.equals(namespace)) {
- continue;
- }
- DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace);
- }
- }
-
- private static void performScopedReset(Context context, @NonNull String failedPackage) {
- RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
- Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(
- failedPackage);
- // If we can't find namespaces affected for current package,
- // skip this round of reset.
- if (affectedNamespaces != null) {
- Slog.w(TAG,
- "Performing scoped reset for package: " + failedPackage
- + ", affected namespaces: "
- + Arrays.toString(affectedNamespaces.toArray()));
- Iterator<String> it = affectedNamespaces.iterator();
- while (it.hasNext()) {
- String namespace = it.next();
- // Don't let RescueParty reset the namespace for RescueParty switches.
- if (NAMESPACE_CONFIGURATION.equals(namespace)) {
- continue;
- }
- DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE, namespace);
- }
- }
- }
-
/**
* Handle mitigation action for package failures. This observer will be register to Package
* Watchdog and will receive calls about package failures. This observer is persistent so it
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b35959f..19279a8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -104,6 +104,7 @@
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.DiskInfo;
@@ -1180,6 +1181,7 @@
private void onUserUnlocking(int userId) {
Slog.d(TAG, "onUserUnlocking " + userId);
+ Trace.instant(Trace.TRACE_TAG_SYSTEM_SERVER, "SMS.onUserUnlocking: " + userId);
if (userId != UserHandle.USER_SYSTEM) {
// Check if this user shares media with another user
@@ -1466,6 +1468,8 @@
@Override
public void onVolumeCreated(String volId, int type, String diskId, String partGuid,
int userId) {
+ Trace.instant(Trace.TRACE_TAG_SYSTEM_SERVER,
+ "SMS.onVolumeCreated: " + volId + ", " + userId);
synchronized (mLock) {
final DiskInfo disk = mDisks.get(diskId);
final VolumeInfo vol = new VolumeInfo(volId, type, disk, partGuid);
@@ -2352,6 +2356,7 @@
private void mount(VolumeInfo vol) {
try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "SMS.mount: " + vol.id);
// TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
extendWatchdogTimeout("#mount might be slow");
@@ -2363,6 +2368,8 @@
vol.internalPath = internalPath;
ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+ "SMS.startFuseFileSystem: " + vol.id);
mStorageSessionController.onVolumeMount(pfd, vol);
return true;
} catch (ExternalStorageServiceException e) {
@@ -2375,6 +2382,7 @@
TimeUnit.SECONDS.toMillis(nextResetSeconds));
return false;
} finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
try {
pfd.close();
} catch (Exception e) {
@@ -2386,6 +2394,8 @@
Slog.i(TAG, "Mounted volume " + vol);
} catch (Exception e) {
Slog.wtf(TAG, e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 44aea15..e2ab0d9 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -1603,7 +1603,7 @@
} else {
mPackageToSharedUidAllowList.put(pkgName, sharedUid);
}
- }
+ } break;
case "asl-file": {
String packageName = parser.getAttributeValue(null, "package");
String path = parser.getAttributeValue(null, "path");
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6bd7445..0fa5260 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -127,6 +127,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_EXECUTING;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import android.Manifest;
import android.annotation.IntDef;
@@ -4304,7 +4305,7 @@
// queued up in the app side as they're one way calls. And we'll also hold off
// the service timeout timer until the process is unfrozen.
mAm.mOomAdjuster.updateAppFreezeStateLSP(callerApp, OOM_ADJ_REASON_BIND_SERVICE,
- true);
+ true, UNKNOWN_ADJ);
}
final boolean wasStopped = hostApp == null ? wasStopped(s) : false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d41de38..b23f5f2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -730,7 +730,7 @@
/** Whether some specified important processes are allowed to use FIFO priority. */
boolean mAllowSpecifiedFifoScheduling = true;
- @GuardedBy("this")
+ @GuardedBy("mStrictModeCallbacks")
private final SparseArray<IUnsafeIntentStrictModeCallback>
mStrictModeCallbacks = new SparseArray<>();
@@ -5550,9 +5550,7 @@
for (int i=0; i<intents.length; i++) {
Intent intent = intents[i];
if (intent != null) {
- if (intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
+ intent.prepareToEnterSystemServer();
if (type == ActivityManager.INTENT_SENDER_BROADCAST &&
(intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
throw new IllegalArgumentException(
@@ -5585,7 +5583,6 @@
}
}
intents[i] = new Intent(intent);
- intents[i].removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
}
if (resolvedTypes != null && resolvedTypes.length != intents.length) {
@@ -9538,18 +9535,20 @@
* @param callback The binder used to communicate the violations.
*/
@Override
- public synchronized void registerStrictModeCallback(IBinder callback) {
- int callingPid = Binder.getCallingPid();
- mStrictModeCallbacks.put(callingPid,
- IUnsafeIntentStrictModeCallback.Stub.asInterface(callback));
- try {
- callback.linkToDeath(() -> {
- synchronized (ActivityManagerService.this) {
- mStrictModeCallbacks.remove(callingPid);
- }
- }, 0);
- } catch (RemoteException e) {
- mStrictModeCallbacks.remove(callingPid);
+ public void registerStrictModeCallback(IBinder callback) {
+ final int callingPid = Binder.getCallingPid();
+ synchronized (mStrictModeCallbacks) {
+ mStrictModeCallbacks.put(callingPid,
+ IUnsafeIntentStrictModeCallback.Stub.asInterface(callback));
+ try {
+ callback.linkToDeath(() -> {
+ synchronized (mStrictModeCallbacks) {
+ mStrictModeCallbacks.remove(callingPid);
+ }
+ }, 0);
+ } catch (RemoteException e) {
+ mStrictModeCallbacks.remove(callingPid);
+ }
}
}
@@ -10016,8 +10015,9 @@
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
- boolean shouldAddLogs = logcatLines > 0 || kernelLogLines > 0;
- if (!runSynchronously && shouldAddLogs) {
+ boolean shouldAddLogs = (logcatLines > 0 || kernelLogLines > 0)
+ && (Flags.collectLogcatOnRunSynchronously() || !runSynchronously);
+ if (shouldAddLogs) {
sb.append("\n");
if (logcatLines > 0) {
fetchLogcatBuffers(sb, logcatLines, LOGCAT_TIMEOUT_SEC,
@@ -13961,12 +13961,7 @@
enforceNotIsolatedCaller("startService");
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
if (service != null) {
- // Refuse possible leaked file descriptors
- if (service.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- // Remove existing mismatch flag so it can be properly updated later
- service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+ service.prepareToEnterSystemServer();
}
if (callingPackage == null) {
@@ -14203,12 +14198,7 @@
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
if (service != null) {
- // Refuse possible leaked file descriptors
- if (service.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- // Remove existing mismatch flag so it can be properly updated later
- service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+ service.prepareToEnterSystemServer();
}
if (callingPackage == null) {
@@ -16242,12 +16232,7 @@
final Intent verifyBroadcastLocked(Intent intent) {
if (intent != null) {
- // Refuse possible leaked file descriptors
- if (intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- // Remove existing mismatch flag so it can be properly updated later
- intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+ intent.prepareToEnterSystemServer();
}
int flags = intent.getFlags();
@@ -16307,6 +16292,7 @@
String[] excludedPackages, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
+
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -16320,6 +16306,12 @@
// Permission regimes around sender-supplied broadcast options.
enforceBroadcastOptionPermissionsInternal(bOptions, callingUid);
+ final ComponentName cn = intent.getComponent();
+
+ Trace.traceBegin(
+ Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "broadcastIntent:" + (cn != null ? cn.toString() : intent.getAction()));
+
final long origId = Binder.clearCallingIdentity();
try {
return broadcastIntentLocked(callerApp,
@@ -16330,6 +16322,7 @@
callingPid, userId, BackgroundStartPrivileges.NONE, null, null);
} finally {
Binder.restoreCallingIdentity(origId);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
}
@@ -19930,7 +19923,7 @@
public void triggerUnsafeIntentStrictMode(int callingPid, int type, Intent intent) {
final IUnsafeIntentStrictModeCallback callback;
final Intent i = intent.cloneFilter();
- synchronized (ActivityManagerService.this) {
+ synchronized (mStrictModeCallbacks) {
callback = mStrictModeCallbacks.get(callingPid);
}
if (callback != null) {
@@ -19938,7 +19931,7 @@
try {
callback.onUnsafeIntent(type, i);
} catch (RemoteException e) {
- synchronized (ActivityManagerService.this) {
+ synchronized (mStrictModeCallbacks) {
mStrictModeCallbacks.remove(callingPid);
}
}
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 666e560..47b65eb 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -88,6 +88,7 @@
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.zip.GZIPOutputStream;
@@ -104,9 +105,9 @@
private static final long APP_EXIT_INFO_PERSIST_INTERVAL = TimeUnit.MINUTES.toMillis(30);
/** These are actions that the forEach* should take after each iteration */
- private static final int FOREACH_ACTION_NONE = 0;
- private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
- private static final int FOREACH_ACTION_STOP_ITERATION = 2;
+ @VisibleForTesting static final int FOREACH_ACTION_NONE = 0;
+ @VisibleForTesting static final int FOREACH_ACTION_REMOVE_ITEM = 1;
+ @VisibleForTesting static final int FOREACH_ACTION_STOP_ITERATION = 2;
private static final int APP_EXIT_RAW_INFO_POOL_SIZE = 8;
@@ -125,7 +126,7 @@
private static final String APP_TRACE_FILE_SUFFIX = ".gz";
- private final Object mLock = new Object();
+ @VisibleForTesting final Object mLock = new Object();
/**
* Initialized in {@link #init} and read-only after that.
@@ -410,6 +411,23 @@
}
/**
+ * Certain types of crashes should not be updated. This could end up deleting valuable
+ * information, for example, if a test application crashes and then the `am instrument`
+ * finishes, then the crash whould be replaced with a `reason == USER_REQUESTED`
+ * ApplicationExitInfo from ActivityManager, and the original crash would be lost.
+ */
+ private boolean preventExitInfoUpdate(final ApplicationExitInfo exitInfo) {
+ switch (exitInfo.getReason()) {
+ case ApplicationExitInfo.REASON_ANR:
+ case ApplicationExitInfo.REASON_CRASH:
+ case ApplicationExitInfo.REASON_CRASH_NATIVE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
* Make note when ActivityManagerService decides to kill an application process.
*/
@VisibleForTesting
@@ -418,10 +436,10 @@
ApplicationExitInfo info = getExitInfoLocked(
raw.getPackageName(), raw.getPackageUid(), raw.getPid());
- if (info == null) {
+ if (info == null || preventExitInfoUpdate(info)) {
info = addExitInfoLocked(raw);
} else {
- // always override the existing info since we are now more informational.
+ // Override the existing info since we have more information.
info.setReason(raw.getReason());
info.setSubReason(raw.getSubReason());
info.setStatus(0);
@@ -431,24 +449,8 @@
scheduleLogToStatsdLocked(info, true);
}
- /**
- * Make note when ActivityManagerService gets a recoverable native crash, as the process isn't
- * being killed but the crash should still be added to AppExitInfo. Also, because we're not
- * crashing, don't log out to statsd.
- */
- @VisibleForTesting
- @GuardedBy("mLock")
- void handleNoteAppRecoverableCrashLocked(final ApplicationExitInfo raw) {
- addExitInfoLocked(raw, /* recoverable */ true);
- }
-
@GuardedBy("mLock")
private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw) {
- return addExitInfoLocked(raw, /* recoverable */ false);
- }
-
- @GuardedBy("mLock")
- private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw, boolean recoverable) {
if (!mAppExitInfoLoaded.get()) {
Slog.w(TAG, "Skipping saving the exit info due to ongoing loading from storage");
return null;
@@ -464,13 +466,13 @@
}
}
for (int i = 0; i < packages.length; i++) {
- addExitInfoInnerLocked(packages[i], uid, info, recoverable);
+ addExitInfoInnerLocked(packages[i], uid, info);
}
// SDK sandbox exits are stored under both real and package UID
if (Process.isSdkSandboxUid(uid)) {
for (int i = 0; i < packages.length; i++) {
- addExitInfoInnerLocked(packages[i], raw.getPackageUid(), info, recoverable);
+ addExitInfoInnerLocked(packages[i], raw.getPackageUid(), info);
}
}
@@ -526,72 +528,77 @@
if (k != null) {
uid = k;
}
- ArrayList<ApplicationExitInfo> tlist = mTmpInfoList;
- tlist.clear();
final int targetUid = uid;
+ // Launder the modification bit through a `final` array, as Java doesn't allow you to mutate
+ // a captured boolean inside of a lambda.
+ final boolean[] isModified = {false};
forEachPackageLocked((packageName, records) -> {
AppExitInfoContainer container = records.get(targetUid);
if (container == null) {
return FOREACH_ACTION_NONE;
}
- tlist.clear();
- container.getExitInfoLocked(pid, 1, tlist);
- if (tlist.size() == 0) {
+ mTmpInfoList.clear();
+ container.getExitInfosLocked(pid, /* maxNum */ 0, mTmpInfoList);
+ if (mTmpInfoList.size() == 0) {
return FOREACH_ACTION_NONE;
}
- ApplicationExitInfo info = tlist.get(0);
- if (info.getRealUid() != targetUid) {
- tlist.clear();
- return FOREACH_ACTION_NONE;
- }
- // Okay found it, update its reason.
- updateExistingExitInfoRecordLocked(info, status, reason);
- return FOREACH_ACTION_STOP_ITERATION;
+ for (int i = 0, size = mTmpInfoList.size(); i < size; i++) {
+ ApplicationExitInfo info = mTmpInfoList.get(i);
+ if (info.getRealUid() != targetUid) {
+ continue;
+ }
+ // We only update the most recent `ApplicationExitInfo` for this pid, which will
+ // always be the first one we se as `getExitInfosLocked()` returns them sorted
+ // by most-recent-first.
+ isModified[0] = true;
+ updateExistingExitInfoRecordLocked(info, status, reason);
+ return FOREACH_ACTION_STOP_ITERATION;
+ }
+ return FOREACH_ACTION_NONE;
});
- return tlist.size() > 0;
+ mTmpInfoList.clear();
+ return isModified[0];
}
/**
* Get the exit info with matching package name, filterUid and filterPid (if > 0)
*/
@VisibleForTesting
- void getExitInfo(final String packageName, final int filterUid,
- final int filterPid, final int maxNum, final ArrayList<ApplicationExitInfo> results) {
+ void getExitInfo(final String packageName, final int filterUid, final int filterPid,
+ final int maxNum, final List<ApplicationExitInfo> results) {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- boolean emptyPackageName = TextUtils.isEmpty(packageName);
- if (!emptyPackageName) {
- // fast path
+ if (!TextUtils.isEmpty(packageName)) {
+ // Fast path - just a single package.
AppExitInfoContainer container = mData.get(packageName, filterUid);
if (container != null) {
- container.getExitInfoLocked(filterPid, maxNum, results);
+ container.getExitInfosLocked(filterPid, maxNum, results);
}
- } else {
- // slow path
- final ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
- list.clear();
- // get all packages
- forEachPackageLocked((name, records) -> {
- AppExitInfoContainer container = records.get(filterUid);
- if (container != null) {
- mTmpInfoList.clear();
- list.addAll(container.toListLocked(mTmpInfoList, filterPid));
- }
- return AppExitInfoTracker.FOREACH_ACTION_NONE;
- });
+ return;
+ }
- Collections.sort(list,
- (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
- int size = list.size();
- if (maxNum > 0) {
- size = Math.min(size, maxNum);
+ // Slow path - get all the packages.
+ forEachPackageLocked((name, records) -> {
+ AppExitInfoContainer container = records.get(filterUid);
+ if (container != null) {
+ container.getExitInfosLocked(filterPid, /* maxNum */ 0, results);
}
- for (int i = 0; i < size; i++) {
- results.add(list.get(i));
- }
- list.clear();
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
+
+ // And while the results for each package are sorted, we should
+ // sort over and trim the quantity of global results as well.
+ Collections.sort(
+ results, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
+ if (maxNum <= 0) {
+ return;
+ }
+
+ int elementsToRemove = results.size() - maxNum;
+ for (int i = 0; i < elementsToRemove; i++) {
+ results.removeLast();
}
}
} finally {
@@ -606,12 +613,10 @@
@GuardedBy("mLock")
private ApplicationExitInfo getExitInfoLocked(final String packageName,
final int filterUid, final int filterPid) {
- ArrayList<ApplicationExitInfo> list = mTmpInfoList;
- list.clear();
- getExitInfo(packageName, filterUid, filterPid, 1, list);
-
- ApplicationExitInfo info = list.size() > 0 ? list.get(0) : null;
- list.clear();
+ mTmpInfoList.clear();
+ getExitInfo(packageName, filterUid, filterPid, 1, mTmpInfoList);
+ ApplicationExitInfo info = mTmpInfoList.size() > 0 ? mTmpInfoList.getFirst() : null;
+ mTmpInfoList.clear();
return info;
}
@@ -878,8 +883,7 @@
}
@GuardedBy("mLock")
- private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info,
- boolean recoverable) {
+ private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info) {
AppExitInfoContainer container = mData.get(packageName, uid);
if (container == null) {
container = new AppExitInfoContainer(mAppExitInfoHistoryListSize);
@@ -893,11 +897,7 @@
}
mData.put(packageName, uid, container);
}
- if (recoverable) {
- container.addRecoverableCrashLocked(info);
- } else {
- container.addExitInfoLocked(info);
- }
+ container.addExitInfoLocked(info);
}
@GuardedBy("mLock")
@@ -1205,7 +1205,7 @@
forEachPackageLocked((name, records) -> {
for (int i = records.size() - 1; i >= 0; i--) {
final AppExitInfoContainer container = records.valueAt(i);
- container.forEachRecordLocked((pid, info) -> {
+ container.forEachRecordLocked((info) -> {
final File traceFile = info.getTraceFile();
if (traceFile != null) {
allFiles.remove(traceFile.getName());
@@ -1322,90 +1322,72 @@
* A container class of {@link android.app.ApplicationExitInfo}
*/
final class AppExitInfoContainer {
- private SparseArray<ApplicationExitInfo> mInfos; // index is a pid
- private SparseArray<ApplicationExitInfo> mRecoverableCrashes; // index is a pid
+ private ArrayList<ApplicationExitInfo> mExitInfos;
private int mMaxCapacity;
private int mUid; // Application uid, not isolated uid.
AppExitInfoContainer(final int maxCapacity) {
- mInfos = new SparseArray<ApplicationExitInfo>();
- mRecoverableCrashes = new SparseArray<ApplicationExitInfo>();
+ mExitInfos = new ArrayList<ApplicationExitInfo>();
mMaxCapacity = maxCapacity;
}
+ @VisibleForTesting
@GuardedBy("mLock")
- void getInfosLocked(SparseArray<ApplicationExitInfo> map, final int filterPid,
- final int maxNum, ArrayList<ApplicationExitInfo> results) {
- if (filterPid > 0) {
- ApplicationExitInfo r = map.get(filterPid);
- if (r != null) {
- results.add(r);
+ void getExitInfosLocked(
+ final int filterPid, final int maxNum, List<ApplicationExitInfo> results) {
+ if (mExitInfos.size() == 0) {
+ return;
+ }
+
+ // Most of the callers might only be interested with the most recent
+ // ApplicationExitInfo, and so we can special case an O(n) walk.
+ if (maxNum == 1) {
+ ApplicationExitInfo result = null;
+ for (int i = 0, size = mExitInfos.size(); i < size; i++) {
+ ApplicationExitInfo info = mExitInfos.get(i);
+ if (filterPid > 0 && info.getPid() != filterPid) {
+ continue;
+ }
+
+ if (result == null || result.getTimestamp() < info.getTimestamp()) {
+ result = info;
+ }
}
+ if (result != null) {
+ results.add(result);
+ }
+ return;
+ }
+
+ mTmpInfoList2.clear();
+ if (filterPid <= 0) {
+ mTmpInfoList2.addAll(mExitInfos);
} else {
- final int numRep = map.size();
- if (maxNum <= 0 || numRep <= maxNum) {
- // Return all records.
- for (int i = 0; i < numRep; i++) {
- results.add(map.valueAt(i));
- }
- Collections.sort(results,
- (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
- } else {
- if (maxNum == 1) {
- // Most of the caller might be only interested with the most recent one
- ApplicationExitInfo r = map.valueAt(0);
- for (int i = 1; i < numRep; i++) {
- ApplicationExitInfo t = map.valueAt(i);
- if (r.getTimestamp() < t.getTimestamp()) {
- r = t;
- }
- }
- results.add(r);
- } else {
- // Huh, need to sort it out then.
- ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
- list.clear();
- for (int i = 0; i < numRep; i++) {
- list.add(map.valueAt(i));
- }
- Collections.sort(list,
- (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
- for (int i = 0; i < maxNum; i++) {
- results.add(list.get(i));
- }
- list.clear();
+ for (int i = 0, size = mExitInfos.size(); i < size; i++) {
+ ApplicationExitInfo info = mExitInfos.get(i);
+ if (info.getPid() == filterPid) {
+ mTmpInfoList2.add(info);
}
}
}
- }
- @GuardedBy("mLock")
- void getExitInfoLocked(final int filterPid, final int maxNum,
- ArrayList<ApplicationExitInfo> results) {
- getInfosLocked(mInfos, filterPid, maxNum, results);
- }
-
- @GuardedBy("mLock")
- void addInfoLocked(SparseArray<ApplicationExitInfo> map, ApplicationExitInfo info) {
- int size;
- if ((size = map.size()) >= mMaxCapacity) {
- int oldestIndex = -1;
- long oldestTimeStamp = Long.MAX_VALUE;
- for (int i = 0; i < size; i++) {
- ApplicationExitInfo r = map.valueAt(i);
- if (r.getTimestamp() < oldestTimeStamp) {
- oldestTimeStamp = r.getTimestamp();
- oldestIndex = i;
- }
- }
- if (oldestIndex >= 0) {
- final File traceFile = map.valueAt(oldestIndex).getTraceFile();
- if (traceFile != null) {
- traceFile.delete();
- }
- map.removeAt(oldestIndex);
- }
+ Collections.sort(
+ mTmpInfoList2, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
+ if (maxNum <= 0) {
+ results.addAll(mTmpInfoList2);
+ return;
}
+
+ int elementsToRemove = mTmpInfoList2.size() - maxNum;
+ for (int i = 0; i < elementsToRemove; i++) {
+ mTmpInfoList2.removeLast();
+ }
+ results.addAll(mTmpInfoList2);
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void addExitInfoLocked(ApplicationExitInfo info) {
// Claim the state information if there is any
int uid = info.getPackageUid();
// SDK sandbox app states and app traces are stored under real UID
@@ -1420,24 +1402,39 @@
if (info.getTraceFile() == null) {
info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid));
}
-
info.setAppTraceRetriever(mAppTraceRetriever);
- map.append(pid, info);
+
+ mExitInfos.add(info);
+ if (mExitInfos.size() <= mMaxCapacity) {
+ return;
+ }
+
+ ApplicationExitInfo oldest = null;
+ for (int i = 0, size = mExitInfos.size(); i < size; i++) {
+ ApplicationExitInfo info2 = mExitInfos.get(i);
+ if (oldest == null || info2.getTimestamp() < oldest.getTimestamp()) {
+ oldest = info2;
+ }
+ }
+ File traceFile = oldest.getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ mExitInfos.remove(oldest);
}
@GuardedBy("mLock")
- void addExitInfoLocked(ApplicationExitInfo info) {
- addInfoLocked(mInfos, info);
- }
-
- @GuardedBy("mLock")
- void addRecoverableCrashLocked(ApplicationExitInfo info) {
- addInfoLocked(mRecoverableCrashes, info);
+ ApplicationExitInfo getLastExitInfoForPid(final int pid) {
+ mTmpInfoList.clear();
+ getExitInfosLocked(pid, /* maxNum */ 1, mTmpInfoList);
+ ApplicationExitInfo info = mTmpInfoList.size() == 0 ? null : mTmpInfoList.getFirst();
+ mTmpInfoList.clear();
+ return info;
}
@GuardedBy("mLock")
boolean appendTraceIfNecessaryLocked(final int pid, final File traceFile) {
- final ApplicationExitInfo r = mInfos.get(pid);
+ final ApplicationExitInfo r = getLastExitInfoForPid(pid);
if (r != null) {
r.setTraceFile(traceFile);
r.setAppTraceRetriever(mAppTraceRetriever);
@@ -1447,49 +1444,36 @@
}
@GuardedBy("mLock")
- void destroyLocked(SparseArray<ApplicationExitInfo> map) {
- for (int i = map.size() - 1; i >= 0; i--) {
- ApplicationExitInfo ai = map.valueAt(i);
- final File traceFile = ai.getTraceFile();
+ void destroyLocked() {
+ for (int i = 0, size = mExitInfos.size(); i < size; i++) {
+ ApplicationExitInfo info = mExitInfos.get(i);
+ final File traceFile = info.getTraceFile();
if (traceFile != null) {
traceFile.delete();
}
- ai.setTraceFile(null);
- ai.setAppTraceRetriever(null);
+ info.setTraceFile(null);
+ info.setAppTraceRetriever(null);
}
}
+ /**
+ * Go through each record in an *unspecified* order, execute `callback()` on each element,
+ * and potentially do some action (stopping iteration, removing the element, etc.) based on
+ * the return value of the callback.
+ */
@GuardedBy("mLock")
- void destroyLocked() {
- destroyLocked(mInfos);
- destroyLocked(mRecoverableCrashes);
- }
-
- @GuardedBy("mLock")
- void forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback) {
+ void forEachRecordLocked(final Function<ApplicationExitInfo, Integer> callback) {
if (callback == null) return;
- for (int i = mInfos.size() - 1; i >= 0; i--) {
- switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) {
+ for (int i = mExitInfos.size() - 1; i >= 0; i--) {
+ ApplicationExitInfo info = mExitInfos.get(i);
+ switch (callback.apply(info)) {
case FOREACH_ACTION_STOP_ITERATION: return;
case FOREACH_ACTION_REMOVE_ITEM:
- final File traceFile = mInfos.valueAt(i).getTraceFile();
- if (traceFile != null) {
+ File traceFile;
+ if ((traceFile = info.getTraceFile()) != null) {
traceFile.delete();
}
- mInfos.removeAt(i);
- break;
- }
- }
- for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
- switch (callback.apply(
- mRecoverableCrashes.keyAt(i), mRecoverableCrashes.valueAt(i))) {
- case FOREACH_ACTION_STOP_ITERATION: return;
- case FOREACH_ACTION_REMOVE_ITEM:
- final File traceFile = mRecoverableCrashes.valueAt(i).getTraceFile();
- if (traceFile != null) {
- traceFile.delete();
- }
- mRecoverableCrashes.removeAt(i);
+ mExitInfos.remove(info);
break;
}
}
@@ -1497,30 +1481,20 @@
@GuardedBy("mLock")
void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
- ArrayList<ApplicationExitInfo> list = new ArrayList<ApplicationExitInfo>();
- for (int i = mInfos.size() - 1; i >= 0; i--) {
- list.add(mInfos.valueAt(i));
+ mTmpInfoList.clear();
+ getExitInfosLocked(/* filterPid */ 0, /* maxNum */ 0, mTmpInfoList);
+ for (int i = 0, size = mTmpInfoList.size(); i < size; i++) {
+ mTmpInfoList.get(i).dump(pw, prefix + " ", "#" + i, sdf);
}
- for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
- list.add(mRecoverableCrashes.valueAt(i));
- }
- Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
- int size = list.size();
- for (int i = 0; i < size; i++) {
- list.get(i).dump(pw, prefix + " ", "#" + i, sdf);
- }
+ mTmpInfoList.clear();
}
@GuardedBy("mLock")
void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(AppsExitInfoProto.Package.User.UID, mUid);
- for (int i = 0; i < mInfos.size(); i++) {
- mInfos.valueAt(i).writeToProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO);
- }
- for (int i = 0; i < mRecoverableCrashes.size(); i++) {
- mRecoverableCrashes.valueAt(i).writeToProto(
- proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH);
+ for (int i = 0, size = mExitInfos.size(); i < size; i++) {
+ mExitInfos.get(i).writeToProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO);
}
proto.end(token);
}
@@ -1539,14 +1513,7 @@
case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO: {
ApplicationExitInfo info = new ApplicationExitInfo();
info.readFromProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO);
- mInfos.put(info.getPid(), info);
- break;
- }
- case (int) AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH: {
- ApplicationExitInfo info = new ApplicationExitInfo();
- info.readFromProto(
- proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH);
- mRecoverableCrashes.put(info.getPid(), info);
+ mExitInfos.add(info);
break;
}
}
@@ -1554,24 +1521,6 @@
proto.end(token);
return mUid;
}
-
- @GuardedBy("mLock")
- List<ApplicationExitInfo> toListLocked(List<ApplicationExitInfo> list, int filterPid) {
- if (list == null) {
- list = new ArrayList<ApplicationExitInfo>();
- }
- for (int i = mInfos.size() - 1; i >= 0; i--) {
- if (filterPid == 0 || filterPid == mInfos.keyAt(i)) {
- list.add(mInfos.valueAt(i));
- }
- }
- for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
- if (filterPid == 0 || filterPid == mRecoverableCrashes.keyAt(i)) {
- list.add(mRecoverableCrashes.valueAt(i));
- }
- }
- return list;
- }
}
/**
@@ -1750,7 +1699,11 @@
case MSG_APP_RECOVERABLE_CRASH: {
ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
synchronized (mLock) {
- handleNoteAppRecoverableCrashLocked(raw);
+ // Unlike MSG_APP_KILL, this is a recoverable crash, and
+ // so we want to bypass the statsd app-kill logging.
+ // Hence, call `addExitInfoLocked()` directly instead of
+ // `handleNoteAppKillLocked()`.
+ addExitInfoLocked(raw);
}
recycleRawRecord(raw);
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 1ac37ad..0c14a1c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -130,6 +130,7 @@
import com.android.server.power.stats.BluetoothPowerStatsProcessor;
import com.android.server.power.stats.CameraPowerStatsProcessor;
import com.android.server.power.stats.CpuPowerStatsProcessor;
+import com.android.server.power.stats.CustomEnergyConsumerPowerStatsProcessor;
import com.android.server.power.stats.FlashlightPowerStatsProcessor;
import com.android.server.power.stats.GnssPowerStatsProcessor;
import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
@@ -574,6 +575,15 @@
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+
+ config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
return config;
}
@@ -665,6 +675,10 @@
BatteryConsumer.POWER_COMPONENT_CAMERA,
Flags.streamlinedMiscBatteryStats());
+ // By convention POWER_COMPONENT_ANY represents custom Energy Consumers
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_ANY,
+ Flags.streamlinedMiscBatteryStats());
+
mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 178171d..aeebae4 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -2482,7 +2482,8 @@
ipw.println();
if (dumpConstants) {
- mConstants.dump(ipw);
+ mFgConstants.dump(ipw);
+ mBgConstants.dump(ipw);
}
if (dumpHistory) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 211f952..6433f2c 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -83,6 +83,8 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
@@ -98,8 +100,6 @@
import java.util.Random;
import java.util.Set;
-import dalvik.annotation.optimization.NeverCompile;
-
public final class CachedAppOptimizer {
// Flags stored in the DeviceConfig API.
@@ -248,7 +248,7 @@
private static final int COMPACT_ACTION_ANON_FLAG = 2;
private static final String ATRACE_COMPACTION_TRACK = "Compaction";
- private static final String ATRACE_FREEZER_TRACK = "Freezer";
+ public static final String ATRACE_FREEZER_TRACK = "Freezer";
private static final int FREEZE_BINDER_TIMEOUT_MS = 0;
private static final int FREEZE_DEADLOCK_TIMEOUT_MS = 1000;
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index a8b9e43..4ff1367 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -674,7 +674,9 @@
if (conn != null) {
conn.waiting = true;
}
- cpr.wait(wait);
+ if (wait > 0) {
+ cpr.wait(wait);
+ }
if (cpr.provider == null) {
timedOut = true;
break;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c9e0666..b058bd8 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1653,8 +1653,7 @@
new ComputeOomAdjWindowCallback();
/** These methods are called inline during computeOomAdjLSP(), on the same thread */
- final class ComputeOomAdjWindowCallback
- implements WindowProcessController.ComputeOomAdjCallback {
+ final class ComputeOomAdjWindowCallback {
ProcessRecord app;
int adj;
@@ -1684,8 +1683,7 @@
this.mState = app.mState;
}
- @Override
- public void onVisibleActivity() {
+ void onVisibleActivity(int flags) {
// App has a visible activity; only upgrade adjustment.
if (adj > VISIBLE_APP_ADJ) {
adj = VISIBLE_APP_ADJ;
@@ -1705,12 +1703,17 @@
if (schedGroup < SCHED_GROUP_DEFAULT) {
schedGroup = SCHED_GROUP_DEFAULT;
}
+ if ((flags & WindowProcessController.ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN) != 0) {
+ // Another side of split should be the current global top. Use the same top
+ // priority for this non-top split.
+ schedGroup = SCHED_GROUP_TOP_APP;
+ mAdjType = "resumed-split-screen-activity";
+ }
foregroundActivities = true;
mHasVisibleActivities = true;
}
- @Override
- public void onPausedActivity() {
+ void onPausedActivity() {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
mAdjType = "pause-activity";
@@ -1733,8 +1736,7 @@
mHasVisibleActivities = false;
}
- @Override
- public void onStoppingActivity(boolean finishing) {
+ void onStoppingActivity(boolean finishing) {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
mAdjType = "stop-activity";
@@ -1764,8 +1766,7 @@
mHasVisibleActivities = false;
}
- @Override
- public void onOtherActivity() {
+ void onOtherActivity() {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
mAdjType = "cch-act";
@@ -1832,7 +1833,8 @@
state.setNoKillOnBgRestrictedAndIdle(false);
// If this UID is currently allowlisted, it should not be frozen.
final UidRecord uidRec = app.getUidRecord();
- app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed());
+ app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed(),
+ ProcessCachedOptimizerRecord.SHOULD_NOT_FREEZE_REASON_UID_ALLOWLISTED, mAdjSeq);
}
final int appUid = app.info.uid;
@@ -2672,7 +2674,9 @@
if (client.mOptRecord.shouldNotFreeze()) {
// Propagate the shouldNotFreeze flag down the bindings.
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -2739,7 +2743,10 @@
if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) {
// Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
if (clientAdj < CACHED_APP_MIN_ADJ) {
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | ProcessCachedOptimizerRecord
+ .SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT, mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -2976,7 +2983,10 @@
// bound by an unfrozen app via a WPRI binding has to remain
// unfrozen.
if (clientAdj < CACHED_APP_MIN_ADJ) {
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | ProcessCachedOptimizerRecord
+ .SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY, mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -3117,7 +3127,9 @@
}
if (client.mOptRecord.shouldNotFreeze()) {
// Propagate the shouldNotFreeze flag down the bindings.
- if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+ if (app.mOptRecord.setShouldNotFreeze(true, dryRun,
+ app.mOptRecord.shouldNotFreezeReason()
+ | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -3476,7 +3488,7 @@
changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
}
- updateAppFreezeStateLSP(app, oomAdjReson, false);
+ updateAppFreezeStateLSP(app, oomAdjReson, false, oldOomAdj);
if (state.getReportedProcState() != state.getCurProcState()) {
state.setReportedProcState(state.getCurProcState());
@@ -3873,16 +3885,37 @@
@GuardedBy({"mService", "mProcLock"})
void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason,
- boolean immediate) {
+ boolean immediate, int oldOomAdj) {
if (!mCachedAppOptimizer.useFreezer()) {
return;
}
+ final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+ final ProcessStateRecord state = app.mState;
+ if (Flags.traceUpdateAppFreezeStateLsp()) {
+ final boolean oomAdjChanged =
+ (state.getCurAdj() >= FREEZER_CUTOFF_ADJ ^ oldOomAdj >= FREEZER_CUTOFF_ADJ)
+ || oldOomAdj == UNKNOWN_ADJ;
+ final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq;
+ if ((oomAdjChanged || shouldNotFreezeChanged)
+ && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ CachedAppOptimizer.ATRACE_FREEZER_TRACK,
+ "updateAppFreezeStateLSP " + app.processName
+ + " isFreezeExempt: " + opt.isFreezeExempt()
+ + " isFrozen: " + opt.isFrozen()
+ + " shouldNotFreeze: " + opt.shouldNotFreeze()
+ + " shouldNotFreezeReason: " + opt.shouldNotFreezeReason()
+ + " curAdj: " + state.getCurAdj()
+ + " oldOomAdj: " + oldOomAdj
+ + " immediate: " + immediate);
+ }
+ }
+
if (app.mOptRecord.isFreezeExempt()) {
return;
}
- final ProcessCachedOptimizerRecord opt = app.mOptRecord;
// if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
if (opt.isFrozen() && opt.shouldNotFreeze()) {
mCachedAppOptimizer.unfreezeAppLSP(app,
@@ -3890,7 +3923,6 @@
return;
}
- final ProcessStateRecord state = app.mState;
// Use current adjustment when freezing, set adjustment when unfreezing.
if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen()
&& !opt.shouldNotFreeze()) {
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index ea92571..53643b8 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.annotation.IntDef;
import android.annotation.UptimeMillisLong;
import android.app.ActivityManagerInternal.OomAdjReason;
import android.util.TimeUtils;
@@ -23,14 +24,31 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import java.io.PrintWriter;
-
import dalvik.annotation.optimization.NeverCompile;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The state info of app when it's cached, used by the optimizer.
*/
final class ProcessCachedOptimizerRecord {
+
+ static final int SHOULD_NOT_FREEZE_REASON_NONE = 1;
+ static final int SHOULD_NOT_FREEZE_REASON_UID_ALLOWLISTED = 1 << 1;
+ static final int SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT = 1 << 2;
+ static final int SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY = 1 << 3;
+
+ @IntDef(flag = true, prefix = {"SHOULD_NOT_FREEZE_REASON_"}, value = {
+ SHOULD_NOT_FREEZE_REASON_NONE,
+ SHOULD_NOT_FREEZE_REASON_UID_ALLOWLISTED,
+ SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT,
+ SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ShouldNotFreezeReason {}
+
private final ProcessRecord mApp;
private final ActivityManagerGlobalLock mProcLock;
@@ -112,6 +130,18 @@
private boolean mShouldNotFreeze;
/**
+ * Reason for mShouldNotFreeze being set to a particular value.
+ */
+ @GuardedBy("mProcLock")
+ private @ShouldNotFreezeReason int mShouldNotFreezeReason;
+
+ /**
+ * The value of adjSeq when last time mShouldNotFreeze was set.
+ */
+ @GuardedBy("mProcLock")
+ private int mShouldNotFreezeAdjSeq;
+
+ /**
* Exempt from freezer (now for system apps with INSTALL_PACKAGES permission)
*/
@GuardedBy("mProcLock")
@@ -275,8 +305,19 @@
}
@GuardedBy("mProcLock")
- void setShouldNotFreeze(boolean shouldNotFreeze) {
- setShouldNotFreeze(shouldNotFreeze, false);
+ @ShouldNotFreezeReason int shouldNotFreezeReason() {
+ return mShouldNotFreezeReason;
+ }
+
+ @GuardedBy("mProcLock")
+ int shouldNotFreezeAdjSeq() {
+ return mShouldNotFreezeAdjSeq;
+ }
+
+ @GuardedBy("mProcLock")
+ void setShouldNotFreeze(boolean shouldNotFreeze, @ShouldNotFreezeReason int reason,
+ int adjSeq) {
+ setShouldNotFreeze(shouldNotFreeze, false, reason, adjSeq);
}
/**
@@ -284,10 +325,23 @@
* if it was a real run.
*/
@GuardedBy("mProcLock")
- boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun) {
+ boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun,
+ @ShouldNotFreezeReason int reason, int adjSeq) {
if (dryRun) {
return mFrozen && !shouldNotFreeze;
}
+ if (Flags.traceUpdateAppFreezeStateLsp()) {
+ if (shouldNotFreeze) {
+ reason &= ~SHOULD_NOT_FREEZE_REASON_NONE;
+ } else {
+ reason = SHOULD_NOT_FREEZE_REASON_NONE;
+ }
+
+ if (reason != mShouldNotFreezeReason || shouldNotFreeze != mShouldNotFreeze) {
+ mShouldNotFreezeAdjSeq = adjSeq;
+ }
+ mShouldNotFreezeReason = reason;
+ }
mShouldNotFreeze = shouldNotFreeze;
return false;
}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 7c64298..bc990d9 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -27,6 +27,11 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessRecord.TAG;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
import android.annotation.ElapsedRealtimeLong;
import android.app.ActivityManager;
@@ -1108,6 +1113,7 @@
return mCachedCompatChanges[cachedCompatChangeId] == VALUE_TRUE;
}
+ /** This is only called if the process contains activities and is not the global top. */
@GuardedBy("mService")
void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
int adj, boolean foregroundActivities, boolean hasVisibleActivities, int procState,
@@ -1117,8 +1123,17 @@
}
callback.initialize(mApp, adj, foregroundActivities, hasVisibleActivities, procState,
schedGroup, appUid, logUid, processCurTop);
- final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
- mApp.getWindowProcessController().computeOomAdjFromActivities(callback));
+ final int flags = mApp.getWindowProcessController().getActivityStateFlags();
+
+ if ((flags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0) {
+ callback.onVisibleActivity(flags);
+ } else if ((flags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) {
+ callback.onPausedActivity();
+ } else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {
+ callback.onStoppingActivity((flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
+ } else {
+ callback.onOtherActivity();
+ }
mCachedAdj = callback.adj;
mCachedForegroundActivities = callback.foregroundActivities;
@@ -1128,6 +1143,8 @@
mCachedAdjType = callback.mAdjType;
if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
+ final int taskLayer = flags & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX, taskLayer);
mCachedAdj += minLayer;
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fa0e2ca..30efa3e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2335,6 +2335,14 @@
// Never stop system user
return;
}
+ synchronized(mLock) {
+ final UserState uss = mStartedUsers.get(oldUserId);
+ if (uss == null || uss.state == UserState.STATE_STOPPING
+ || uss.state == UserState.STATE_SHUTDOWN) {
+ // We've stopped (or are stopping) the user anyway, so don't bother scheduling.
+ return;
+ }
+ }
if (oldUserId == mInjector.getUserManagerInternal().getMainUserId()) {
// MainUser is currently special for things like Docking, so we'll exempt it for now.
Slogf.i(TAG, "Exempting user %d from being stopped due to inactivity by virtue "
@@ -2371,6 +2379,12 @@
// We'll soon want to switch to this user, so don't kill it now.
return;
}
+ final UserInfo currentOrTargetUser = getCurrentUserLU();
+ if (currentOrTargetUser != null && currentOrTargetUser.isGuest()) {
+ // Don't kill any background users for the sake of a Guest. Just reschedule instead.
+ scheduleStopOfBackgroundUser(userId);
+ return;
+ }
Slogf.i(TAG, "Stopping background user %d due to inactivity", userId);
stopUsersLU(userId, /* allowDelayedLocking= */ true, null, null);
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index bb52857..0dfa330 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -125,6 +125,14 @@
}
flag {
+ name: "trace_update_app_freeze_state_lsp"
+ namespace: "backstage_power"
+ description: "Output process oom adj and other metadata to trace in updateAppFreezeStateLSP"
+ bug: "345547733"
+ is_fixed_read_only: true
+}
+
+flag {
name: "simplify_process_traversal"
namespace: "backstage_power"
description: "Simplify the OomAdjuster's process traversal mechanism."
@@ -152,3 +160,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "collect_logcat_on_run_synchronously"
+ namespace: "dropbox"
+ description: "Allow logcat collection on synchronous dropbox collection"
+ bug: "324222683"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 1bb7922..f61bd60 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2790,8 +2790,9 @@
* have information on them.
*/
private static boolean isOpAllowedForUid(int uid) {
+ int appId = UserHandle.getAppId(uid);
return Flags.runtimePermissionAppopsMappingEnabled()
- && (uid == Process.ROOT_UID || uid == Process.SYSTEM_UID);
+ && (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID);
}
@Override
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index bce1830..a8c269d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -414,7 +414,8 @@
}
if (!mScoManagedByAudio) {
boolean isBtScoRequested = isBluetoothScoRequested();
- if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
+ if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive()
+ || !mBtHelper.isBluetoothScoRequestedInternally())) {
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
+ uid);
@@ -1148,13 +1149,14 @@
}
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
- if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
- }
synchronized (mBluetoothAudioStateLock) {
+ boolean isBtScoRequested = isBluetoothScoRequested();
+ Log.i(TAG, "setBluetoothScoOn: " + on + ", mBluetoothScoOn: "
+ + mBluetoothScoOn + ", isBtScoRequested: " + isBtScoRequested
+ + ", from: " + eventSource);
mBluetoothScoOn = on;
updateAudioHalBluetoothState();
- postUpdateCommunicationRouteClient(isBluetoothScoRequested(), eventSource);
+ postUpdateCommunicationRouteClient(isBtScoRequested, eventSource);
}
}
@@ -2791,12 +2793,13 @@
return mDeviceInventory.getImmutableDeviceInventory();
}
- void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) {
- mDeviceInventory.addOrUpdateDeviceSAStateInInventory(deviceState);
+ void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState, boolean syncInventory) {
+ mDeviceInventory.addOrUpdateDeviceSAStateInInventory(deviceState, syncInventory);
}
- void addOrUpdateBtAudioDeviceCategoryInInventory(AdiDeviceState deviceState) {
- mDeviceInventory.addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ void addOrUpdateBtAudioDeviceCategoryInInventory(
+ AdiDeviceState deviceState, boolean syncInventory) {
+ mDeviceInventory.addOrUpdateAudioDeviceCategoryInInventory(deviceState, syncInventory);
}
@Nullable
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index ba7aee0..6ff4a61 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -135,9 +135,10 @@
* AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
* @param deviceState the device to update
*/
- void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState) {
+ void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState, boolean syncInventory) {
synchronized (mDeviceInventoryLock) {
- mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, (oldState, newState) -> {
+ mDeviceInventory.merge(deviceState.getDeviceId(), deviceState,
+ (oldState, newState) -> {
oldState.setHasHeadTracker(newState.hasHeadTracker());
oldState.setHeadTrackerEnabled(newState.isHeadTrackerEnabled());
oldState.setSAEnabled(newState.isSAEnabled());
@@ -145,7 +146,9 @@
});
checkDeviceInventorySize_l();
}
- mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ if (syncInventory) {
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ }
}
/**
@@ -196,7 +199,8 @@
* AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
* @param deviceState the device to update
*/
- void addOrUpdateAudioDeviceCategoryInInventory(AdiDeviceState deviceState) {
+ void addOrUpdateAudioDeviceCategoryInInventory(
+ AdiDeviceState deviceState, boolean syncInventory) {
AtomicBoolean updatedCategory = new AtomicBoolean(false);
synchronized (mDeviceInventoryLock) {
if (automaticBtDeviceType()) {
@@ -218,7 +222,9 @@
if (updatedCategory.get()) {
mDeviceBroker.postUpdatedAdiDeviceState(deviceState, false /*initSA*/);
}
- mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ if (syncInventory) {
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState);
+ }
}
void addAudioDeviceWithCategoryInInventoryIfNeeded(@NonNull String address,
@@ -235,14 +241,14 @@
boolean bleCategoryFound = false;
AdiDeviceState deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLE_HEADSET);
if (deviceState != null) {
- addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ addOrUpdateAudioDeviceCategoryInInventory(deviceState, true /*syncInventory*/);
btCategory = deviceState.getAudioDeviceCategory();
bleCategoryFound = true;
}
deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLUETOOTH_A2DP);
if (deviceState != null) {
- addOrUpdateAudioDeviceCategoryInInventory(deviceState);
+ addOrUpdateAudioDeviceCategoryInInventory(deviceState, true /*syncInventory*/);
int a2dpCategory = deviceState.getAudioDeviceCategory();
if (bleCategoryFound && a2dpCategory != btCategory) {
Log.w(TAG, "Found different audio device category for A2DP and BLE profiles with "
@@ -269,23 +275,43 @@
}
/**
- * synchronize AdiDeviceState for LE devices in the same group
+ * Synchronize AdiDeviceState for LE devices in the same group
+ * or BT classic devices with the same address.
+ * @param updatedDevice the device state to synchronize or null.
+ * Called with null once after the device inventory and spatializer helper
+ * have been initialized to resync all devices.
*/
void onSynchronizeAdiDevicesInInventory(AdiDeviceState updatedDevice) {
synchronized (mDevicesLock) {
synchronized (mDeviceInventoryLock) {
- boolean found = false;
- found |= synchronizeBleDeviceInInventory(updatedDevice);
- if (automaticBtDeviceType()) {
- found |= synchronizeDeviceProfilesInInventory(updatedDevice);
- }
- if (found) {
- mDeviceBroker.postPersistAudioDeviceSettings();
+ if (updatedDevice != null) {
+ onSynchronizeAdiDeviceInInventory_l(updatedDevice);
+ } else {
+ for (AdiDeviceState ads : mDeviceInventory.values()) {
+ onSynchronizeAdiDeviceInInventory_l(ads);
+ }
}
}
}
}
+ /**
+ * Synchronize AdiDeviceState for LE devices in the same group
+ * or BT classic devices with the same address.
+ * @param updatedDevice the device state to synchronize.
+ */
+ @GuardedBy({"mDevicesLock", "mDeviceInventoryLock"})
+ void onSynchronizeAdiDeviceInInventory_l(AdiDeviceState updatedDevice) {
+ boolean found = false;
+ found |= synchronizeBleDeviceInInventory(updatedDevice);
+ if (automaticBtDeviceType()) {
+ found |= synchronizeDeviceProfilesInInventory(updatedDevice);
+ }
+ if (found) {
+ mDeviceBroker.postPersistAudioDeviceSettings();
+ }
+ }
+
@GuardedBy("mDeviceInventoryLock")
private void checkDeviceInventorySize_l() {
if (mDeviceInventory.size() > MAX_DEVICE_INVENTORY_ENTRIES) {
@@ -595,6 +621,9 @@
mDeviceName = TextUtils.emptyIfNull(deviceName);
mDeviceAddress = TextUtils.emptyIfNull(address);
mDeviceIdentityAddress = TextUtils.emptyIfNull(identityAddress);
+ if (mDeviceIdentityAddress.isEmpty()) {
+ mDeviceIdentityAddress = mDeviceAddress;
+ }
mDeviceCodecFormat = codecFormat;
mGroupId = groupId;
mPeerDeviceAddress = TextUtils.emptyIfNull(peerAddress);
@@ -2951,8 +2980,8 @@
// Note if the device is not compatible with spatialization mode or the device
// type is not canonical, it will be ignored in {@link SpatializerHelper}.
if (devState != null) {
- addOrUpdateDeviceSAStateInInventory(devState);
- addOrUpdateAudioDeviceCategoryInInventory(devState);
+ addOrUpdateDeviceSAStateInInventory(devState, false /*syncInventory*/);
+ addOrUpdateAudioDeviceCategoryInInventory(devState, false /*syncInventory*/);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 684cb24..dcce96b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -783,9 +783,11 @@
AudioSystem.DEVICE_OUT_HDMI_EARC
));
+ private final Object mAbsoluteVolumeDeviceInfoMapLock = new Object();
// Devices where the framework sends a full scale audio signal, and controls the volume of
// the external audio system separately.
// For possible volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
+ @GuardedBy("mAbsoluteVolumeDeviceInfoMapLock")
Map<Integer, AbsoluteVolumeDeviceInfo> mAbsoluteVolumeDeviceInfoMap = new ArrayMap<>();
/**
@@ -3715,9 +3717,8 @@
int oldIndex = mStreamStates[streamType].getIndex(device);
// Check if the volume adjustment should be handled by an absolute volume controller instead
- if (isAbsoluteVolumeDevice(device)
- && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
- AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
+ if (isAbsoluteVolumeDevice(device) && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
+ final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(device);
if (info.mHandlesVolumeAdjustment) {
dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction,
keyEventMode);
@@ -3784,7 +3785,7 @@
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
} else if (isAbsoluteVolumeDevice(device)
&& (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
- AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
+ final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(device);
dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
}
@@ -4787,7 +4788,7 @@
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
} else if (isAbsoluteVolumeDevice(device)
&& ((flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0)) {
- AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
+ final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(device);
dispatchAbsoluteVolumeChanged(streamType, info, index);
}
@@ -7575,7 +7576,8 @@
if (register) {
AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(
device, volumes, cb, handlesVolumeAdjustment, deviceVolumeBehavior);
- AbsoluteVolumeDeviceInfo oldInfo = mAbsoluteVolumeDeviceInfoMap.get(deviceOut);
+ final AbsoluteVolumeDeviceInfo oldInfo = getAbsoluteVolumeDeviceInfo(deviceOut);
+
boolean volumeBehaviorChanged = (oldInfo == null)
|| (oldInfo.mDeviceVolumeBehavior != deviceVolumeBehavior);
if (volumeBehaviorChanged) {
@@ -7735,8 +7737,10 @@
if (mAbsVolumeMultiModeCaseDevices.contains(audioSystemDeviceOut)) {
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
}
- if (mAbsoluteVolumeDeviceInfoMap.containsKey(audioSystemDeviceOut)) {
- return mAbsoluteVolumeDeviceInfoMap.get(audioSystemDeviceOut).mDeviceVolumeBehavior;
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ if (mAbsoluteVolumeDeviceInfoMap.containsKey(audioSystemDeviceOut)) {
+ return mAbsoluteVolumeDeviceInfoMap.get(audioSystemDeviceOut).mDeviceVolumeBehavior;
+ }
}
if (isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
@@ -9635,6 +9639,9 @@
case MSG_INIT_SPATIALIZER:
onInitSpatializer();
+ // the device inventory can only be synchronized after the
+ // spatializer has been initialized
+ mDeviceBroker.postSynchronizeAdiDevicesInInventory(null);
mAudioEventWakeLock.release();
break;
@@ -11390,7 +11397,8 @@
deviceState.setAudioDeviceCategory(btAudioDeviceCategory);
- mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState);
+ mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(
+ deviceState, true /*syncInventory*/);
mDeviceBroker.postPersistAudioDeviceSettings();
mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes(),
@@ -11694,7 +11702,7 @@
static final int LOG_NB_EVENTS_VOLUME = 100;
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
static final int LOG_NB_EVENTS_SPATIAL = 30;
- static final int LOG_NB_EVENTS_SOUND_DOSE = 30;
+ static final int LOG_NB_EVENTS_SOUND_DOSE = 50;
static final int LOG_NB_EVENTS_LOUDNESS_CODEC = 30;
@@ -11774,10 +11782,12 @@
}
private Set<Integer> getAbsoluteVolumeDevicesWithBehavior(int behavior) {
- return mAbsoluteVolumeDeviceInfoMap.entrySet().stream()
- .filter(entry -> entry.getValue().mDeviceVolumeBehavior == behavior)
- .map(Map.Entry::getKey)
- .collect(Collectors.toSet());
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.entrySet().stream()
+ .filter(entry -> entry.getValue().mDeviceVolumeBehavior == behavior)
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toSet());
+ }
}
private String dumpDeviceTypes(@NonNull Set<Integer> deviceTypes) {
@@ -14270,14 +14280,26 @@
}
/**
+ * Returns the input device which uses absolute volume behavior, including its variants,
+ * or {@code null} if there is no mapping for the device type
+ */
+ @Nullable
+ private AbsoluteVolumeDeviceInfo getAbsoluteVolumeDeviceInfo(int deviceType) {
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.get(deviceType);
+ }
+ }
+
+ /**
* Returns whether the input device uses absolute volume behavior, including its variants.
* For included volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
- *
- * This is distinct from Bluetooth A2DP absolute volume behavior
+ * <p>This is distinct from Bluetooth A2DP absolute volume behavior
* ({@link #isA2dpAbsoluteVolumeDevice}).
*/
private boolean isAbsoluteVolumeDevice(int deviceType) {
- return mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType);
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType);
+ }
}
/**
@@ -14389,7 +14411,9 @@
+ AudioDeviceVolumeManager.volumeBehaviorName(info.mDeviceVolumeBehavior)
);
}
- mAbsoluteVolumeDeviceInfoMap.put(audioSystemDeviceOut, info);
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ mAbsoluteVolumeDeviceInfoMap.put(audioSystemDeviceOut, info);
+ }
}
private AbsoluteVolumeDeviceInfo removeAudioSystemDeviceOutFromAbsVolumeDevices(
@@ -14398,7 +14422,10 @@
Log.d(TAG, "Removing DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " from mAbsoluteVolumeDeviceInfoMap");
}
- return mAbsoluteVolumeDeviceInfoMap.remove(audioSystemDeviceOut);
+
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.remove(audioSystemDeviceOut);
+ }
}
//====================
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 749044e..8ea28be 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -577,6 +577,7 @@
static final int DOSE_REPEAT_5X = 2;
static final int DOSE_ACCUMULATION_START = 3;
static final int LOWER_VOLUME_TO_RS1 = 4;
+ static final int UPDATE_ABS_VOLUME_ATTENUATION = 5;
final int mEventType;
final float mFloatValue;
@@ -608,6 +609,10 @@
return new SoundDoseEvent(LOWER_VOLUME_TO_RS1, 0 /*ignored*/, 0 /*ignored*/);
}
+ static SoundDoseEvent getAbsVolumeAttenuationEvent(float attenuation, int device) {
+ return new SoundDoseEvent(UPDATE_ABS_VOLUME_ATTENUATION, attenuation, device);
+ }
+
@Override
public String eventToString() {
switch (mEventType) {
@@ -623,6 +628,10 @@
return "CSD accumulating: RS2 entered";
case LOWER_VOLUME_TO_RS1:
return "CSD lowering volume to RS1";
+ case UPDATE_ABS_VOLUME_ATTENUATION:
+ return String.format(java.util.Locale.US,
+ "Updating CSD absolute volume attenuation on device %d with %.2f dB ",
+ mLongValue, mFloatValue);
}
return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 991f94b..b6125cc 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -142,7 +142,6 @@
private static final int SCO_MODE_MAX = 2;
private static final int BT_HEARING_AID_GAIN_MIN = -128;
- private static final int BT_LE_AUDIO_MIN_VOL = 0;
private static final int BT_LE_AUDIO_MAX_VOL = 255;
// BtDevice constants currently rolling out under flag protection. Use own
@@ -211,8 +210,7 @@
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -373,12 +371,10 @@
return codecAndChanged;
}
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package*/ synchronized void onReceiveBtEvent(Intent intent) {
final String action = intent.getAction();
- Log.i(TAG, "onReceiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE,
android.bluetooth.BluetoothDevice.class);
@@ -397,14 +393,15 @@
}
/**
- * Exclusively called from AudioDeviceBroker when handling MSG_L_RECEIVED_BT_EVENT
+ * Exclusively called from AudioDeviceBroker (with mSetModeLock held)
+ * when handling MSG_L_RECEIVED_BT_EVENT in {@link #onReceiveBtEvent(Intent)}
* as part of the serialization of the communication route selection
*/
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
private void onScoAudioStateChanged(int state) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ Log.i(TAG, "onScoAudioStateChanged state: " + state + " mScoAudioState: " + mScoAudioState);
if (mDeviceBroker.isScoManagedByAudio()) {
switch (state) {
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
@@ -488,16 +485,19 @@
== BluetoothHeadset.STATE_AUDIO_CONNECTED;
}
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ /*package*/ synchronized boolean isBluetoothScoRequestedInternally() {
+ return mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
+ || mScoAudioState == SCO_STATE_ACTIVATE_REQ;
+ }
+
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
@NonNull String eventSource) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
}
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
@@ -569,8 +569,7 @@
mScoConnectionState = state;
}
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package*/ synchronized void resetBluetoothSco() {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -579,8 +578,7 @@
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package*/ synchronized void onBtProfileDisconnected(int profile) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile " + BluetoothProfile.getProfileName(profile)
@@ -644,8 +642,7 @@
MyLeAudioCallback mLeAudioCallback = null;
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy "
@@ -782,8 +779,7 @@
}
}
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
private void onHeadsetProfileConnected(@NonNull BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -926,8 +922,7 @@
return btDevice == null ? "(null)" : btDevice.getAnonymizedAddress();
}
- // @GuardedBy("mDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ // Called locked by ADeviceBroker.mSetModeLock -> AudioDeviceBroker.mDeviceStateLock
/*package */ synchronized void onSetBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "onSetBtScoActiveDevice: " + getAnonymizedAddress(mBluetoothHeadsetDevice)
+ " -> " + getAnonymizedAddress(btDevice));
@@ -1128,6 +1123,9 @@
//-----------------------------------------------------
// Utilities
+
+ // suppress warning due to generic Intent passed as param
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
private void sendStickyBroadcastToAll(Intent intent) {
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index d26ba48..88268cd 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -662,8 +662,7 @@
synchronized(mPlayerLock) {
pw.println("\n playback listeners:");
for (PlayMonitorClient pmc : mClients) {
- pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)")
- + pmc.toString());
+ pw.println(" " + pmc);
}
pw.println("\n");
// all players
@@ -1031,7 +1030,8 @@
if (pcdb == null) {
return;
}
- final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
+ final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged,
+ Binder.getCallingUid(), Binder.getCallingPid());
if (pmc.init()) {
mClients.add(pmc);
}
@@ -1103,10 +1103,22 @@
private boolean mIsReleased = false;
@GuardedBy("this")
private int mErrorCount = 0;
+ private final int mUid;
+ private final int mPid;
- PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
+ PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged,
+ int uid, int pid) {
mDispatcherCb = pcdb;
mIsPrivileged = isPrivileged;
+ mUid = uid;
+ mPid = pid;
+ }
+
+ @Override
+ public String toString() {
+ return "PlayMonitorClient:"
+ + (isPrivileged() ? "S" : "P")
+ + " uid:" + mUid + " pid:" + mPid;
}
@Override
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index e28ae95..ded93e6 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -39,6 +39,7 @@
import android.media.ISoundDose;
import android.media.ISoundDoseCallback;
import android.media.SoundDoseRecord;
+import android.media.VolumeInfo;
import android.os.Binder;
import android.os.Message;
import android.os.RemoteException;
@@ -642,9 +643,9 @@
if (index > safeIndex) {
streamState.setIndex(safeIndex, deviceType, caller,
true /*hasModifyAudioSettings*/);
- mAudioHandler.sendMessageAtTime(
+ mAudioHandler.sendMessage(
mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, deviceType,
- /*arg2=*/0, streamState), /*delay=*/0);
+ /*arg2=*/0, streamState));
}
}
}
@@ -685,8 +686,11 @@
/*package*/ void disableSafeMediaVolume(String callingPackage) {
synchronized (mSafeMediaVolumeStateLock) {
final long identity = Binder.clearCallingIdentity();
- setSafeMediaVolumeEnabled(false, callingPackage);
- Binder.restoreCallingIdentity(identity);
+ try {
+ setSafeMediaVolumeEnabled(false, callingPackage);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
if (mPendingVolumeCommand != null) {
mAudioService.onSetStreamVolume(mPendingVolumeCommand.mStreamType,
@@ -700,6 +704,7 @@
}
}
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
/*package*/ void scheduleMusicActiveCheck() {
synchronized (mSafeMediaVolumeStateLock) {
cancelMusicActiveCheck();
@@ -895,6 +900,8 @@
try {
if (!isAbsoluteVolume) {
+ mLogger.enqueue(
+ SoundDoseEvent.getAbsVolumeAttenuationEvent(/*attenuation=*/0.f, device));
// remove any possible previous attenuation
soundDose.updateAttenuation(/* attenuationDB= */0.f, device);
@@ -903,10 +910,11 @@
if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC
&& safeDevicesContains(device)) {
- soundDose.updateAttenuation(
- -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
- (newIndex + 5) / 10,
- device), device);
+ float attenuationDb = -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
+ (newIndex + 5) / 10, device);
+ mLogger.enqueue(
+ SoundDoseEvent.getAbsVolumeAttenuationEvent(attenuationDb, device));
+ soundDose.updateAttenuation(attenuationDb, device);
}
} catch (RemoteException e) {
Log.e(TAG, "Could not apply the attenuation for MEL calculation with volume index "
@@ -1031,10 +1039,9 @@
mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
}
- mAudioHandler.sendMessageAtTime(
+ mAudioHandler.sendMessage(
mAudioHandler.obtainMessage(MSG_PERSIST_SAFE_VOLUME_STATE,
- persistedState, /*arg2=*/0,
- /*obj=*/null), /*delay=*/0);
+ persistedState, /*arg2=*/0, /*obj=*/null));
}
private void updateCsdEnabled(String caller) {
@@ -1195,8 +1202,8 @@
sanitizeDoseRecords_l();
- mAudioHandler.sendMessageAtTime(mAudioHandler.obtainMessage(MSG_PERSIST_CSD_VALUES,
- /* arg1= */0, /* arg2= */0, /* obj= */null), /* delay= */0);
+ mAudioHandler.sendMessage(mAudioHandler.obtainMessage(MSG_PERSIST_CSD_VALUES,
+ /* arg1= */0, /* arg2= */0, /* obj= */null));
mLogger.enqueue(SoundDoseEvent.getDoseUpdateEvent(currentCsd, totalDuration));
}
@@ -1312,23 +1319,32 @@
}
/** Called when handling MSG_LOWER_VOLUME_TO_RS1 */
+ @SuppressWarnings("AndroidFrameworkRequiresPermission")
private void onLowerVolumeToRs1() {
- mLogger.enqueue(SoundDoseEvent.getLowerVolumeToRs1Event());
final ArrayList<AudioDeviceAttributes> devices = mAudioService.getDevicesForAttributesInt(
- new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(), true);
- final int nativeDeviceType;
- final AudioDeviceAttributes ada;
- if (!devices.isEmpty()) {
- ada = devices.get(0);
- nativeDeviceType = ada.getInternalType();
- } else {
- nativeDeviceType = AudioSystem.DEVICE_OUT_USB_HEADSET;
- ada = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_USB_HEADSET, "");
+ new AudioAttributes.Builder().setUsage(
+ AudioAttributes.USAGE_MEDIA).build(), /*forVolume=*/true);
+ if (devices.isEmpty()) {
+ Log.e(TAG, "Cannot lower the volume to RS1, no devices registered for USAGE_MEDIA");
+ return;
}
- final int index = safeMediaVolumeIndex(nativeDeviceType);
- mAudioService.setStreamVolumeWithAttributionInt(STREAM_MUSIC, index / 10, /*flags*/ 0, ada,
- mContext.getOpPackageName(), /*attributionTag=*/null,
- true /*canChangeMuteAndUpdateController*/);
+ final AudioDeviceAttributes ada = devices.get(0);
+ final int nativeDeviceType = ada.getInternalType();
+ final int index = safeMediaVolumeIndex(nativeDeviceType) / 10;
+ final VolumeInfo curVolume = mAudioService.getDeviceVolume(
+ new VolumeInfo.Builder(STREAM_MUSIC).build(), ada,
+ /*callingPackage=*/"sounddosehelper");
+
+ if (index < curVolume.getVolumeIndex()) {
+ mLogger.enqueue(SoundDoseEvent.getLowerVolumeToRs1Event());
+ mAudioService.setStreamVolumeWithAttributionInt(STREAM_MUSIC, index, /*flags*/ 0, ada,
+ mContext.getOpPackageName(), /*attributionTag=*/null,
+ /*canChangeMuteAndUpdateController=*/true);
+ } else {
+ Log.i(TAG, "The current volume " + curVolume.getVolumeIndex()
+ + " for device type " + nativeDeviceType
+ + " is already smaller or equal to the safe index volume " + index);
+ }
}
// StreamVolumeCommand contains the information needed to defer the process of
@@ -1348,9 +1364,9 @@
@Override
public String toString() {
- return new StringBuilder().append("{streamType=").append(mStreamType).append(",index=")
- .append(mIndex).append(",flags=").append(mFlags).append(",device=")
- .append(mDevice).append('}').toString();
+ return "{streamType=" + mStreamType
+ + ",index=" + mIndex + ",flags=" + mFlags
+ + ",device=" + mDevice + "}";
}
}
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index cae1695..9265ff2 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -568,7 +568,8 @@
updatedDevice = new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
ada.getAddress());
initSAState(updatedDevice);
- mDeviceBroker.addOrUpdateDeviceSAStateInInventory(updatedDevice);
+ mDeviceBroker.addOrUpdateDeviceSAStateInInventory(
+ updatedDevice, true /*syncInventory*/);
}
if (updatedDevice != null) {
onRoutingUpdated();
@@ -723,7 +724,7 @@
new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
ada.getAddress());
initSAState(deviceState);
- mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState);
+ mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState, true /*syncInventory*/);
mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 0e81eb9..8e8a037 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -31,8 +31,8 @@
import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
-import android.content.ContentResolver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -233,6 +233,7 @@
private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
private static final boolean DEFAULT_APP_ENABLED = true;
private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
+ private static final boolean DEFAULT_MANDATORY_BIOMETRICS_STATUS = false;
// Some devices that shipped before S already have face-specific settings. Instead of
// migrating, which is complicated, let's just keep using the existing settings.
@@ -253,6 +254,8 @@
Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED);
private final Uri BIOMETRIC_APP_ENABLED =
Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED);
+ private final Uri MANDATORY_BIOMETRICS_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.MANDATORY_BIOMETRICS);
private final ContentResolver mContentResolver;
private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
@@ -260,6 +263,7 @@
private final Map<Integer, Boolean> mBiometricEnabledOnKeyguard = new HashMap<>();
private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>();
private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
+ private final Map<Integer, Boolean> mMandatoryBiometricsEnabled = new HashMap<>();
/**
* Creates a content observer.
@@ -281,6 +285,9 @@
mUseLegacyFaceOnlySettings =
Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q
&& hasFace && !hasFingerprint;
+ mMandatoryBiometricsEnabled.put(context.getUserId(), Settings.Secure.getIntForUser(
+ mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS,
+ DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0, context.getUserId()) != 0);
updateContentObserver();
}
@@ -311,6 +318,10 @@
false /* notifyForDescendants */,
this /* observer */,
UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(MANDATORY_BIOMETRICS_ENABLED,
+ false /* notifyForDescendants */,
+ this /* observer */,
+ UserHandle.USER_ALL);
}
@Override
@@ -353,6 +364,12 @@
Settings.Secure.BIOMETRIC_APP_ENABLED,
DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
userId) != 0);
+ } else if (MANDATORY_BIOMETRICS_ENABLED.equals(uri)) {
+ mMandatoryBiometricsEnabled.put(userId, Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.MANDATORY_BIOMETRICS,
+ DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0 /* default */,
+ userId) != 0);
}
}
@@ -394,6 +411,11 @@
}
}
+ public boolean getMandatoryBiometricsEnabledForUser(int userId) {
+ return mMandatoryBiometricsEnabled.getOrDefault(userId,
+ DEFAULT_MANDATORY_BIOMETRICS_STATUS);
+ }
+
void notifyEnabledOnKeyguardCallbacks(int userId) {
List<EnabledOnKeyguardCallback> callbacks = mCallbacks;
for (int i = 0; i < callbacks.size(); i++) {
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index f085647..b9e6563 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -29,11 +29,13 @@
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.PromptInfo;
import android.os.RemoteException;
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.R;
import com.android.server.biometrics.sensors.LockoutTracker;
import java.lang.annotation.Retention;
@@ -59,6 +61,7 @@
static final int BIOMETRIC_LOCKOUT_TIMED = 10;
static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
static final int BIOMETRIC_SENSOR_PRIVACY_ENABLED = 12;
+ static final int MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR = 13;
private static final String TAG = "BiometricService/PreAuthInfo";
final boolean credentialRequested;
// Sensors that can be used for this request (e.g. strong enough, enrolled, enabled).
@@ -73,12 +76,14 @@
private final boolean mBiometricRequested;
private final int mBiometricStrengthRequested;
private final BiometricCameraManager mBiometricCameraManager;
+ private final boolean mOnlyMandatoryBiometricsRequested;
private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
boolean credentialRequested, List<BiometricSensor> eligibleSensors,
List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
- boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
- Context context, BiometricCameraManager biometricCameraManager) {
+ PromptInfo promptInfo, int userId, Context context,
+ BiometricCameraManager biometricCameraManager,
+ boolean isOnlyMandatoryBiometricsRequested) {
mBiometricRequested = biometricRequested;
mBiometricStrengthRequested = biometricStrengthRequested;
mBiometricCameraManager = biometricCameraManager;
@@ -87,10 +92,11 @@
this.eligibleSensors = eligibleSensors;
this.ineligibleSensors = ineligibleSensors;
this.credentialAvailable = credentialAvailable;
- this.confirmationRequested = confirmationRequested;
- this.ignoreEnrollmentState = ignoreEnrollmentState;
+ this.confirmationRequested = promptInfo.isConfirmationRequested();
+ this.ignoreEnrollmentState = promptInfo.isIgnoreEnrollmentState();
this.userId = userId;
this.context = context;
+ this.mOnlyMandatoryBiometricsRequested = isOnlyMandatoryBiometricsRequested;
}
static PreAuthInfo create(ITrustManager trustManager,
@@ -102,7 +108,16 @@
BiometricCameraManager biometricCameraManager)
throws RemoteException {
- final boolean confirmationRequested = promptInfo.isConfirmationRequested();
+ final boolean isOnlyMandatoryBiometricsRequested = promptInfo.getAuthenticators()
+ == BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
+
+ if (dropCredentialFallback(promptInfo.getAuthenticators(),
+ settingObserver.getMandatoryBiometricsEnabledForUser(userId),
+ trustManager)) {
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
+ }
+
final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
final boolean credentialRequested = Utils.isCredentialRequested(promptInfo);
@@ -150,8 +165,27 @@
}
return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
- eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
- promptInfo.isIgnoreEnrollmentState(), userId, context, biometricCameraManager);
+ eligibleSensors, ineligibleSensors, credentialAvailable, promptInfo, userId,
+ context, biometricCameraManager, isOnlyMandatoryBiometricsRequested);
+ }
+
+ private static boolean dropCredentialFallback(int authenticators,
+ boolean isMandatoryBiometricsEnabled, ITrustManager trustManager) {
+ final boolean isMandatoryBiometricsRequested =
+ (authenticators & BiometricManager.Authenticators.MANDATORY_BIOMETRICS)
+ == BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
+ if (Flags.mandatoryBiometrics() && isMandatoryBiometricsEnabled
+ && isMandatoryBiometricsRequested) {
+ try {
+ final boolean isInSignificantPlace = trustManager.isInSignificantPlace();
+ return !isInSignificantPlace;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception while trying to check "
+ + "if user is in a trusted location.");
+ }
+ }
+
+ return false;
}
/**
@@ -353,6 +387,25 @@
status = CREDENTIAL_NOT_ENROLLED;
}
}
+ } else if (Flags.mandatoryBiometrics() && mOnlyMandatoryBiometricsRequested) {
+ if (!eligibleSensors.isEmpty()) {
+ for (BiometricSensor sensor : eligibleSensors) {
+ modality |= sensor.modality;
+ }
+
+ if (modality == TYPE_FACE && cameraPrivacyEnabled) {
+ // If the only modality requested is face, credential is unavailable,
+ // and the face sensor privacy is enabled then return
+ // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
+ //
+ // Note: This sensor will not be eligible for calls to authenticate.
+ status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
+ } else {
+ status = AUTHENTICATOR_OK;
+ }
+ } else {
+ status = MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR;
+ }
} else if (mBiometricRequested) {
if (!eligibleSensors.isEmpty()) {
for (BiometricSensor sensor : eligibleSensors) {
@@ -509,7 +562,8 @@
CREDENTIAL_NOT_ENROLLED,
BIOMETRIC_LOCKOUT_TIMED,
BIOMETRIC_LOCKOUT_PERMANENT,
- BIOMETRIC_SENSOR_PRIVACY_ENABLED})
+ BIOMETRIC_SENSOR_PRIVACY_ENABLED,
+ MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR})
@Retention(RetentionPolicy.SOURCE)
@interface AuthenticatorStatus {
}
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 4af30a9..df29ca4 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -140,6 +140,14 @@
}
/**
+ * @param authenticators composed of one or more values from {@link Authenticators}
+ * @return true if mandatory biometrics is requested
+ */
+ static boolean isMandatoryBiometricsRequested(@Authenticators.Types int authenticators) {
+ return (authenticators & Authenticators.MANDATORY_BIOMETRICS) != 0;
+ }
+
+ /**
* @param promptInfo should be first processed by
* {@link #combineAuthenticatorBundles(PromptInfo)}
* @return true if device credential is allowed.
@@ -242,7 +250,8 @@
// Check if any of the non-biometric and non-credential bits are set. If so, this is
// invalid.
final int testBits = ~(Authenticators.DEVICE_CREDENTIAL
- | Authenticators.BIOMETRIC_MIN_STRENGTH);
+ | Authenticators.BIOMETRIC_MIN_STRENGTH
+ | Authenticators.MANDATORY_BIOMETRICS);
if ((authenticators & testBits) != 0) {
Slog.e(BiometricService.TAG, "Non-biometric, non-credential bits found."
+ " Authenticators: " + authenticators);
@@ -259,6 +268,8 @@
return true;
} else if (biometricBits == Authenticators.BIOMETRIC_WEAK) {
return true;
+ } else if (isMandatoryBiometricsRequested(authenticators)) {
+ return true;
}
Slog.e(BiometricService.TAG, "Unsupported biometric flags. Authenticators: "
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
index 6a2731d..5a493e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
@@ -120,11 +120,13 @@
// Register each sensor individually with BiometricService
final List<P> allProps = new ArrayList<>();
for (T provider : providers) {
- final List<P> props = provider.getSensorProperties();
- for (P prop : props) {
- registerService(biometricService, prop);
+ if(provider != null) {
+ final List<P> props = provider.getSensorProperties();
+ for (P prop : props) {
+ registerService(biometricService, prop);
+ }
+ allProps.addAll(props);
}
- allProps.addAll(props);
}
finishRegistration(providers, allProps);
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index 9467d6f..a3c68f9 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -590,15 +590,9 @@
|| isVendorIdentifierType(id.type);
}
- private static boolean isValidHalProgramInfo(ProgramInfo info) {
- return isValidHalProgramSelector(info.selector)
- && isValidLogicallyTunedTo(info.logicallyTunedTo)
- && isValidPhysicallyTunedTo(info.physicallyTunedTo);
- }
-
@Nullable
static RadioManager.ProgramInfo programInfoFromHalProgramInfo(ProgramInfo info) {
- if (!isValidHalProgramInfo(info)) {
+ if (!isValidHalProgramSelector(info.selector)) {
return null;
}
Collection<ProgramSelector.Identifier> relatedContent = new ArrayList<>();
@@ -624,6 +618,15 @@
);
}
+ @Nullable
+ static RadioManager.ProgramInfo tunedProgramInfoFromHalProgramInfo(ProgramInfo info) {
+ if (!isValidLogicallyTunedTo(info.logicallyTunedTo)
+ || !isValidPhysicallyTunedTo(info.physicallyTunedTo)) {
+ return null;
+ }
+ return programInfoFromHalProgramInfo(info);
+ }
+
static ProgramFilter filterToHalProgramFilter(@Nullable ProgramList.Filter filter) {
if (filter == null) {
filter = new ProgramList.Filter();
@@ -686,8 +689,10 @@
if (!programSelectorMeetsSdkVersionRequirement(info.getSelector(), uid)) {
return false;
}
- if (!identifierMeetsSdkVersionRequirement(info.getLogicallyTunedTo(), uid)
- || !identifierMeetsSdkVersionRequirement(info.getPhysicallyTunedTo(), uid)) {
+ if ((info.getLogicallyTunedTo() != null
+ && !identifierMeetsSdkVersionRequirement(info.getLogicallyTunedTo(), uid))
+ || (info.getPhysicallyTunedTo() != null
+ && !identifierMeetsSdkVersionRequirement(info.getPhysicallyTunedTo(), uid))) {
return false;
}
Iterator<ProgramSelector.Identifier> relatedContentIt = info.getRelatedContent().iterator();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 03e347a..4edd441 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -121,7 +121,7 @@
public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
fireLater(() -> {
RadioManager.ProgramInfo currentProgramInfo =
- ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+ ConversionUtils.tunedProgramInfoFromHalProgramInfo(halProgramInfo);
Objects.requireNonNull(currentProgramInfo,
"Program info from AIDL HAL is invalid");
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 0afca92..73aa14b 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -251,7 +251,7 @@
}
private void registerVirtualDeviceListener() {
- if (mVirtualDeviceListener != null) {
+ if (mVdm == null || mVirtualDeviceListener != null) {
return;
}
mVirtualDeviceListener = new VirtualDeviceManager.VirtualDeviceListener() {
@@ -891,7 +891,8 @@
Slog.e(TAG, "RemoteException calling UserManager: " + e);
return null;
}
- if (deviceId != DEVICE_ID_DEFAULT && !mVdm.isValidVirtualDeviceId(deviceId)) {
+ if (deviceId != DEVICE_ID_DEFAULT
+ && mVdm != null && !mVdm.isValidVirtualDeviceId(deviceId)) {
Slog.w(TAG, "getClipboardLocked called with invalid (possibly released) deviceId "
+ deviceId);
return null;
@@ -1467,8 +1468,8 @@
return;
}
// Don't notify if this access is coming from the privileged app which owns the device.
- if (clipboard.deviceId != DEVICE_ID_DEFAULT && mVdmInternal.getDeviceOwnerUid(
- clipboard.deviceId) == uid) {
+ if (clipboard.deviceId != DEVICE_ID_DEFAULT && mVdmInternal != null
+ && mVdmInternal.getDeviceOwnerUid(clipboard.deviceId) == uid) {
return;
}
// Don't notify if already notified for this uid and clip.
@@ -1519,7 +1520,7 @@
private ArraySet<Context> getToastContexts(Clipboard clipboard) throws IllegalStateException {
ArraySet<Context> contexts = new ArraySet<>();
- if (clipboard.deviceId != DEVICE_ID_DEFAULT) {
+ if (mVdmInternal != null && clipboard.deviceId != DEVICE_ID_DEFAULT) {
DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index b179783..6e38733 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -20,6 +20,8 @@
import android.annotation.Nullable;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.VirtualDevice;
+import android.companion.virtual.VirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.sensor.VirtualSensor;
import android.content.Context;
import android.os.LocaleList;
@@ -180,4 +182,14 @@
* exists, as long as one may have existed or can be created.
*/
public abstract @NonNull Set<String> getAllPersistentDeviceIds();
+
+ /**
+ * Creates a virtual device where applications can launch and receive input events injected by
+ * the creator.
+ *
+ * <p>A Companion Device Manager association is not required. Only the system may create such
+ * virtual devices.</p>
+ */
+ public abstract @NonNull VirtualDeviceManager.VirtualDevice createVirtualDevice(
+ @NonNull VirtualDeviceParams params);
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index f8fd0a0..22b85d4 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -83,9 +83,10 @@
@VisibleForTesting
PlatformCompat(Context context, CompatConfig compatConfig,
- AndroidBuildClassifier buildClassifier) {
+ AndroidBuildClassifier buildClassifier,
+ ChangeReporter changeReporter) {
mContext = context;
- mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
+ mChangeReporter = changeReporter;
mCompatConfig = compatConfig;
mBuildClassifier = buildClassifier;
@@ -96,8 +97,11 @@
@EnforcePermission(LOG_COMPAT_CHANGE)
public void reportChange(long changeId, ApplicationInfo appInfo) {
super.reportChange_enforcePermission();
-
- reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED);
+ reportChangeInternal(
+ changeId,
+ appInfo.uid,
+ appInfo.isSystemApp(),
+ ChangeReporter.STATE_LOGGED);
}
@Override
@@ -108,7 +112,11 @@
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo != null) {
- reportChangeInternal(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED);
+ reportChangeInternal(
+ changeId,
+ appInfo.uid,
+ appInfo.isSystemApp(),
+ ChangeReporter.STATE_LOGGED);
}
}
@@ -117,7 +125,7 @@
public void reportChangeByUid(long changeId, int uid) {
super.reportChangeByUid_enforcePermission();
- reportChangeInternal(changeId, uid, ChangeReporter.STATE_LOGGED);
+ reportChangeInternal(changeId, uid, false, ChangeReporter.STATE_LOGGED);
}
/**
@@ -128,8 +136,8 @@
* @param uid of the user
* @param state of the change - enabled/disabled/logged
*/
- private void reportChangeInternal(long changeId, int uid, int state) {
- mChangeReporter.reportChange(uid, changeId, state, true);
+ private void reportChangeInternal(long changeId, int uid, boolean isKnownSystemApp, int state) {
+ mChangeReporter.reportChange(uid, changeId, state, isKnownSystemApp, true);
}
@Override
@@ -190,7 +198,11 @@
if (appInfo != null) {
boolean isTargetingLatestSdk =
mCompatConfig.isChangeTargetingLatestSdk(c, appInfo.targetSdkVersion);
- mChangeReporter.reportChange(appInfo.uid, changeId, state, isTargetingLatestSdk);
+ mChangeReporter.reportChange(appInfo.uid,
+ changeId,
+ state,
+ appInfo.isSystemApp(),
+ isTargetingLatestSdk);
}
return enabled;
}
diff --git a/services/core/java/com/android/server/crashrecovery/TEST_MAPPING b/services/core/java/com/android/server/crashrecovery/TEST_MAPPING
new file mode 100644
index 0000000..4a66bac
--- /dev/null
+++ b/services/core/java/com/android/server/crashrecovery/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.RescuePartyTest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index 651828b..7d26004 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -131,6 +131,25 @@
}
/**
+ * Sets the brightness. Does not send update event to listeners.
+ * @param brightness The value to which the brightness is to be set.
+ */
+ public void setBrightnessNoNotify(float brightness) {
+ if (Float.isNaN(brightness)) {
+ Slog.w(TAG, "Attempting to init invalid brightness");
+ return;
+ }
+ synchronized (mSyncRoot) {
+ if (brightness != mBrightness) {
+ mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(),
+ brightness, mUserSerial
+ );
+ }
+ mBrightness = brightness;
+ }
+ }
+
+ /**
* @return The brightness for the default display in nits. Used when the underlying display
* device has changed but we want to persist the nit value.
*/
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2d5f38e..43c1f24f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -598,10 +598,11 @@
FoldSettingProvider foldSettingProvider = new FoldSettingProvider(context,
new SettingsWrapper(),
new FoldLockSettingAvailabilityProvider(context.getResources()));
+ Looper displayThreadLooper = DisplayThread.get().getLooper();
mInjector = injector;
mContext = context;
mFlags = injector.getFlags();
- mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
+ mHandler = new DisplayManagerHandler(displayThreadLooper);
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
@@ -609,7 +610,7 @@
mDisplayDeviceRepo, new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags);
mDisplayModeDirector = new DisplayModeDirector(
context, mHandler, mFlags, mDisplayDeviceConfigProvider);
- mBrightnessSynchronizer = new BrightnessSynchronizer(mContext,
+ mBrightnessSynchronizer = new BrightnessSynchronizer(mContext, displayThreadLooper,
mFlags.isBrightnessIntRangeUserPerceptionEnabled());
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
@@ -1548,16 +1549,20 @@
int flags = virtualDisplayConfig.getFlags();
if (virtualDevice != null) {
final VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
- try {
- if (!vdm.isValidVirtualDeviceId(virtualDevice.getDeviceId())) {
- throw new SecurityException("Invalid virtual device");
+ if (vdm != null) {
+ try {
+ if (!vdm.isValidVirtualDeviceId(virtualDevice.getDeviceId())) {
+ throw new SecurityException("Invalid virtual device");
+ }
+ } catch (RemoteException ex) {
+ throw new SecurityException("Unable to validate virtual device");
}
- } catch (RemoteException ex) {
- throw new SecurityException("Unable to validate virtual device");
+ final VirtualDeviceManagerInternal localVdm =
+ getLocalService(VirtualDeviceManagerInternal.class);
+ if (localVdm != null) {
+ flags |= localVdm.getBaseVirtualDisplayFlags(virtualDevice);
+ }
}
- final VirtualDeviceManagerInternal localVdm =
- getLocalService(VirtualDeviceManagerInternal.class);
- flags |= localVdm.getBaseVirtualDisplayFlags(virtualDevice);
}
if (surface != null && surface.isSingleBuffered()) {
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index 0fef55d..a188e79 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -84,6 +84,14 @@
}
@Override
+ public void cancelBlockScreenOn() {
+ if (mDisplayOffloader == null) {
+ return;
+ }
+ mDisplayOffloader.cancelBlockScreenOn();
+ }
+
+ @Override
public float[] getAutoBrightnessLevels(int mode) {
if (mode < 0 || mode > AUTO_BRIGHTNESS_MODE_MAX) {
throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 65a729a..de9715a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -587,7 +587,8 @@
mUniqueDisplayId,
mThermalBrightnessThrottlingDataId,
logicalDisplay.getPowerThrottlingDataIdLocked(),
- mDisplayDeviceConfig), mContext, flags, mSensorManager);
+ mDisplayDeviceConfig,
+ mDisplayId), mContext, flags, mSensorManager);
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
mAutomaticBrightnessStrategy =
@@ -892,7 +893,8 @@
// will call updatePowerState if needed.
mBrightnessClamperController.onDisplayChanged(
new BrightnessClamperController.DisplayDeviceData(uniqueId,
- thermalBrightnessThrottlingDataId, powerThrottlingDataId, config));
+ thermalBrightnessThrottlingDataId, powerThrottlingDataId,
+ config, mDisplayId));
if (changed) {
updatePowerState();
@@ -1565,7 +1567,9 @@
// even if they range changes what it means in absolute terms.
mDisplayBrightnessController.updateScreenBrightnessSetting(
MathUtils.constrain(unthrottledBrightnessState,
- clampedState.getMinBrightness(), clampedState.getMaxBrightness()));
+ clampedState.getMinBrightness(), clampedState.getMaxBrightness()),
+ Math.min(mBrightnessRangeController.getCurrentBrightnessMax(),
+ clampedState.getMaxBrightness()));
}
// The current brightness to use has been calculated at this point, and HbmController should
@@ -2108,6 +2112,17 @@
Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_BY_DISPLAYOFFLOAD_TRACE_NAME, 0);
}
+ private void cancelUnblockScreenOnByDisplayOffload() {
+ if (mDisplayOffloadSession == null) {
+ return;
+ }
+ if (mPendingScreenOnUnblockerByDisplayOffload != null) {
+ // Already unblocked.
+ return;
+ }
+ mDisplayOffloadSession.cancelBlockScreenOn();
+ }
+
private boolean setScreenState(int state, @Display.StateReason int reason) {
return setScreenState(state, reason, false /*reportOnly*/);
}
@@ -2124,6 +2139,9 @@
blockScreenOnByDisplayOffload(mDisplayOffloadSession);
} else if (!isOn && mScreenTurningOnWasBlockedByDisplayOffload) {
// No longer turning screen on, so unblock previous screen on blocking immediately.
+ if (mFlags.isOffloadSessionCancelBlockScreenOnEnabled()) {
+ cancelUnblockScreenOnByDisplayOffload();
+ }
unblockScreenOnByDisplayOffload();
mScreenTurningOnWasBlockedByDisplayOffload = false;
}
@@ -2455,12 +2473,20 @@
@Override
public void setBrightness(float brightness) {
- mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness));
+ // After HBMController and NBMController migration to Clampers framework
+ // currentBrightnessMax should be taken from clampers controller
+ // TODO(b/263362199)
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness),
+ mBrightnessRangeController.getCurrentBrightnessMax());
}
@Override
public void setBrightness(float brightness, int userSerial) {
- mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness), userSerial);
+ // After HBMController and NBMController migration to Clampers framework
+ // currentBrightnessMax should be taken from clampers controller
+ // TODO(b/263362199)
+ mDisplayBrightnessController.setBrightness(clampScreenBrightness(brightness), userSerial,
+ mBrightnessRangeController.getCurrentBrightnessMax());
}
@Override
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 44846f3..6e0b9cf 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -49,6 +49,7 @@
import android.view.SurfaceControl;
import com.android.internal.R;
+import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -1442,8 +1443,9 @@
}
public static class Injector {
- // Native callback.
+ // Ensure the callback is kept to preserve native weak reference lifecycle semantics.
@SuppressWarnings("unused")
+ @KeepForWeakReference
private ProxyDisplayEventReceiver mReceiver;
public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) {
mReceiver = new ProxyDisplayEventReceiver(looper, listener);
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 4982a0b0..c632e77 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -313,13 +313,18 @@
/**
* Notifies the brightnessSetting to persist the supplied brightness value.
*/
- public void setBrightness(float brightnessValue) {
+ public void setBrightness(float brightnessValue, float maxBrightness) {
// Update the setting, which will eventually call back into DPC to have us actually
// update the display with the new value.
mBrightnessSetting.setBrightness(brightnessValue);
if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
float nits = convertToNits(brightnessValue);
- if (nits >= 0) {
+ float currentlyStoredNits = mBrightnessSetting.getBrightnessNitsForDefaultDisplay();
+ // Don't override settings if the brightness is set to max, but the currently
+ // stored value is greater. On multi-screen device, when switching between a
+ // screen with a wider brightness range and one with a narrower brightness range,
+ // the stored value shouldn't change.
+ if (nits >= 0 && !(brightnessValue == maxBrightness && currentlyStoredNits > nits)) {
mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
}
}
@@ -328,15 +333,15 @@
/**
* Notifies the brightnessSetting to persist the supplied brightness value for a user.
*/
- public void setBrightness(float brightnessValue, int userSerial) {
+ public void setBrightness(float brightnessValue, int userSerial, float maxBrightness) {
mBrightnessSetting.setUserSerial(userSerial);
- setBrightness(brightnessValue);
+ setBrightness(brightnessValue, maxBrightness);
}
/**
* Sets the current screen brightness, and notifies the BrightnessSetting about the change.
*/
- public void updateScreenBrightnessSetting(float brightnessValue) {
+ public void updateScreenBrightnessSetting(float brightnessValue, float maxBrightness) {
synchronized (mLock) {
if (!BrightnessUtils.isValidBrightnessValue(brightnessValue)
|| brightnessValue == mCurrentScreenBrightness) {
@@ -345,7 +350,7 @@
setCurrentScreenBrightnessLocked(brightnessValue);
}
notifyCurrentScreenBrightness();
- setBrightness(brightnessValue);
+ setBrightness(brightnessValue, maxBrightness);
}
/**
@@ -582,7 +587,7 @@
float brightnessForDefaultDisplay = getBrightnessFromNits(
brightnessNitsForDefaultDisplay);
if (BrightnessUtils.isValidBrightnessValue(brightnessForDefaultDisplay)) {
- mBrightnessSetting.setBrightness(brightnessForDefaultDisplay);
+ mBrightnessSetting.setBrightnessNoNotify(brightnessForDefaultDisplay);
currentBrightnessSetting = brightnessForDefaultDisplay;
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 101ad30..2206402 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -23,23 +23,17 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Resources;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.util.IndentingPrintWriter;
import android.util.Slog;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig;
@@ -50,30 +44,22 @@
import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
-import com.android.server.display.utils.AmbientFilter;
-import com.android.server.display.utils.AmbientFilterFactory;
-import com.android.server.display.utils.DebugUtils;
-import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
/**
* Clampers controller, all in DisplayControllerHandler
*/
public class BrightnessClamperController {
private static final String TAG = "BrightnessClamperController";
- // To enable these logs, run:
- // 'adb shell setprop persist.log.tag.BrightnessClamperController DEBUG && adb reboot'
- private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
- public static final float INVALID_LUX = -1f;
private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
private final Handler mHandler;
- private final SensorManager mSensorManager;
+ private final LightSensorController mLightSensorController;
+
private final ClamperChangeListener mClamperChangeListenerExternal;
private final Executor mExecutor;
private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers;
@@ -85,70 +71,49 @@
private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
@Nullable
private Type mClamperType = null;
- private final SensorEventListener mLightSensorListener;
- private Sensor mRegisteredLightSensor = null;
- private Sensor mLightSensor;
- private String mLightSensorType;
- private String mLightSensorName;
- private AmbientFilter mAmbientFilter;
- private final DisplayDeviceConfig mDisplayDeviceConfig;
- private final Resources mResources;
- private final int mLightSensorRate;
- private final Injector mInjector;
private boolean mClamperApplied = false;
+ private final LightSensorController.LightSensorListener mLightSensorListener =
+ new LightSensorController.LightSensorListener() {
+ @Override
+ public void onAmbientLuxChange(float lux) {
+ mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux));
+ }
+ };
+
public BrightnessClamperController(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
DisplayManagerFlags flags, SensorManager sensorManager) {
- this(null, handler, clamperChangeListener, data, context, flags, sensorManager);
+ this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager);
}
@VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
DisplayManagerFlags flags, SensorManager sensorManager) {
- mInjector = injector == null ? new Injector() : injector;
- mDeviceConfigParameterProvider = mInjector.getDeviceConfigParameterProvider();
+ mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mHandler = handler;
- mSensorManager = sensorManager;
- mDisplayDeviceConfig = data.mDisplayDeviceConfig;
- mLightSensorListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- long now = SystemClock.elapsedRealtime();
- mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp),
- event.values[0]);
- final float lux = mAmbientFilter.getEstimate(now);
- mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux));
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // unused
- }
- };
+ mLightSensorController = injector.getLightSensorController(sensorManager, context,
+ mLightSensorListener, mHandler);
mClamperChangeListenerExternal = clamperChangeListener;
mExecutor = new HandlerExecutor(handler);
- mResources = context.getResources();
- mLightSensorRate = context.getResources().getInteger(
- R.integer.config_autoBrightnessLightSensorRate);
Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap;
-
ClamperChangeListener clamperChangeListenerInternal = () -> {
if (!mHandler.hasCallbacks(clamperChangeRunnableInternal)) {
mHandler.post(clamperChangeRunnableInternal);
}
};
- mClampers = mInjector.getClampers(handler, clamperChangeListenerInternal, data, flags,
+ mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
context);
- mModifiers = mInjector.getModifiers(flags, context, handler, clamperChangeListener,
- data.mDisplayDeviceConfig, mSensorManager);
+ mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener,
+ data.mDisplayDeviceConfig);
mOnPropertiesChangedListener =
properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
+ mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId());
start();
}
@@ -156,7 +121,9 @@
* Should be called when display changed. Forwards the call to individual clampers
*/
public void onDisplayChanged(DisplayDeviceData data) {
+ mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId());
mClampers.forEach(clamper -> clamper.onDisplayChanged(data));
+ adjustLightSensorSubscription();
}
/**
@@ -184,9 +151,9 @@
}
if (displayState != STATE_ON) {
- unregisterSensorListener();
+ mLightSensorController.stop();
} else {
- maybeRegisterLightSensor();
+ adjustLightSensorSubscription();
}
for (int i = 0; i < mModifiers.size(); i++) {
@@ -231,9 +198,8 @@
writer.println(" mBrightnessCap: " + mBrightnessCap);
writer.println(" mClamperType: " + mClamperType);
writer.println(" mClamperApplied: " + mClamperApplied);
- writer.println(" mLightSensor=" + mLightSensor);
- writer.println(" mRegisteredLightSensor=" + mRegisteredLightSensor);
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
+ mLightSensorController.dump(ipw);
mClampers.forEach(clamper -> clamper.dump(ipw));
mModifiers.forEach(modifier -> modifier.dump(ipw));
}
@@ -245,6 +211,7 @@
public void stop() {
mDeviceConfigParameterProvider.removeOnPropertiesChangedListener(
mOnPropertiesChangedListener);
+ mLightSensorController.stop();
mClampers.forEach(BrightnessClamper::stop);
mModifiers.forEach(BrightnessStateModifier::stop);
}
@@ -281,10 +248,15 @@
if (!mClampers.isEmpty()) {
mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
mExecutor, mOnPropertiesChangedListener);
- reloadLightSensorData(mDisplayDeviceConfig);
- mLightSensor = mInjector.getLightSensor(
- mSensorManager, mLightSensorType, mLightSensorName);
- maybeRegisterLightSensor();
+ }
+ adjustLightSensorSubscription();
+ }
+
+ private void adjustLightSensorSubscription() {
+ if (mModifiers.stream().anyMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
+ mLightSensorController.restart();
+ } else {
+ mLightSensorController.stop();
}
}
@@ -323,7 +295,7 @@
List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
Handler handler, ClamperChangeListener listener,
- DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) {
+ DisplayDeviceConfig displayDeviceConfig) {
List<BrightnessStateModifier> modifiers = new ArrayList<>();
modifiers.add(new DisplayDimModifier(context));
modifiers.add(new BrightnessLowPowerModeModifier());
@@ -335,11 +307,12 @@
return modifiers;
}
- Sensor getLightSensor(SensorManager sensorManager, String type, String name) {
- return SensorUtils.findSensor(sensorManager, type,
- name, Sensor.TYPE_LIGHT);
+ LightSensorController getLightSensorController(SensorManager sensorManager,
+ Context context, LightSensorController.LightSensorListener listener,
+ Handler handler) {
+ return new LightSensorController(sensorManager, context.getResources(),
+ listener, handler);
}
-
}
/**
@@ -354,17 +327,21 @@
private final String mThermalThrottlingDataId;
@NonNull
private final String mPowerThrottlingDataId;
-
+ @NonNull
private final DisplayDeviceConfig mDisplayDeviceConfig;
+ private final int mDisplayId;
+
public DisplayDeviceData(@NonNull String uniqueDisplayId,
@NonNull String thermalThrottlingDataId,
@NonNull String powerThrottlingDataId,
- @NonNull DisplayDeviceConfig displayDeviceConfig) {
+ @NonNull DisplayDeviceConfig displayDeviceConfig,
+ int displayId) {
mUniqueDisplayId = uniqueDisplayId;
mThermalThrottlingDataId = thermalThrottlingDataId;
mPowerThrottlingDataId = powerThrottlingDataId;
mDisplayDeviceConfig = displayDeviceConfig;
+ mDisplayId = displayId;
}
@@ -412,55 +389,18 @@
}
@NonNull
+ @Override
public SensorData getTempSensor() {
return mDisplayDeviceConfig.getTempSensor();
}
- }
- private void maybeRegisterLightSensor() {
- if (mModifiers.stream().noneMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
- return;
+ @NonNull
+ SensorData getAmbientLightSensor() {
+ return mDisplayDeviceConfig.getAmbientLightSensor();
}
- if (mRegisteredLightSensor == mLightSensor) {
- return;
- }
-
- if (mRegisteredLightSensor != null) {
- unregisterSensorListener();
- }
-
- mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, mResources);
- mSensorManager.registerListener(mLightSensorListener,
- mLightSensor, mLightSensorRate * 1000, mHandler);
- mRegisteredLightSensor = mLightSensor;
-
- if (DEBUG) {
- Slog.d(TAG, "maybeRegisterLightSensor");
- }
- }
-
- private void unregisterSensorListener() {
- mSensorManager.unregisterListener(mLightSensorListener);
- mRegisteredLightSensor = null;
- mModifiers.forEach(mModifier -> mModifier.setAmbientLux(INVALID_LUX)); // set lux to invalid
- if (DEBUG) {
- Slog.d(TAG, "unregisterSensorListener");
- }
- }
-
- private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) {
- // The displayDeviceConfig (ddc) contains display specific preferences. When loaded,
- // it naturally falls back to the global config.xml.
- if (displayDeviceConfig != null
- && displayDeviceConfig.getAmbientLightSensor() != null) {
- // This covers both the ddc and the config.xml fallback
- mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type;
- mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name;
- } else if (mLightSensorName == null && mLightSensorType == null) {
- mLightSensorType = mResources.getString(
- com.android.internal.R.string.config_displayLightSensorType);
- mLightSensorName = "";
+ int getDisplayId() {
+ return mDisplayId;
}
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/LightSensorController.java b/services/core/java/com/android/server/display/brightness/clamper/LightSensorController.java
new file mode 100644
index 0000000..d89dd28
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/clamper/LightSensorController.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.Display;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.config.SensorData;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.display.utils.DebugUtils;
+import com.android.server.display.utils.SensorUtils;
+
+import java.io.PrintWriter;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Manages light sensor subscription and notifies its listener about ambient lux changes
+ */
+public class LightSensorController {
+ private static final String TAG = "LightSensorController";
+
+ // To enable these logs, run:
+ // 'adb shell setprop persist.log.tag.LightSensorController DEBUG && adb reboot'
+ private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+ static final float INVALID_LUX = -1f;
+
+ private final SensorManager mSensorManager;
+ private final LightSensorListener mLightSensorListener;
+ private final Handler mHandler;
+ private final Injector mInjector;
+ private final AmbientFilter mAmbientFilter;
+
+ private Sensor mLightSensor;
+ private Sensor mRegisteredLightSensor = null;
+ private final int mLightSensorRate;
+
+ private final SensorEventListener mLightSensorEventListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ long now = mInjector.getTime();
+ mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp),
+ event.values[0]);
+ final float lux = mAmbientFilter.getEstimate(now);
+ mLightSensorListener.onAmbientLuxChange(lux);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // unused
+ }
+ };
+
+ LightSensorController(SensorManager sensorManager, Resources resources,
+ LightSensorListener listener, Handler handler) {
+ this(sensorManager, resources, listener, handler, new Injector());
+ }
+
+ @VisibleForTesting
+ LightSensorController(SensorManager sensorManager, Resources resources,
+ LightSensorListener listener, Handler handler, Injector injector) {
+ mSensorManager = sensorManager;
+ mLightSensorRate = injector.getLightSensorRate(resources);
+ mAmbientFilter = injector.getAmbientFilter(resources);
+ mLightSensorListener = listener;
+ mHandler = handler;
+ mInjector = injector;
+ }
+
+ void restart() {
+ if (mRegisteredLightSensor == mLightSensor) {
+ return;
+ }
+ if (mRegisteredLightSensor != null) {
+ stop();
+ }
+ if (mLightSensor == null) {
+ return;
+ }
+
+ mSensorManager.registerListener(mLightSensorEventListener,
+ mLightSensor, mLightSensorRate * 1000, mHandler);
+ mRegisteredLightSensor = mLightSensor;
+
+ if (DEBUG) {
+ Slog.d(TAG, "restart");
+ }
+ }
+
+ void stop() {
+ if (mRegisteredLightSensor == null) {
+ return;
+ }
+ mSensorManager.unregisterListener(mLightSensorEventListener);
+ mRegisteredLightSensor = null;
+ mAmbientFilter.clear();
+ mLightSensorListener.onAmbientLuxChange(INVALID_LUX);
+ if (DEBUG) {
+ Slog.d(TAG, "stop");
+ }
+ }
+
+ void configure(SensorData sensorData, int displayId) {
+ final int fallbackType = displayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
+ mLightSensor = mInjector.getLightSensor(mSensorManager, sensorData, fallbackType);
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println("LightSensorController");
+ writer.println(" mLightSensor=" + mLightSensor);
+ writer.println(" mRegisteredLightSensor=" + mRegisteredLightSensor);
+ }
+
+ static class Injector {
+ @Nullable
+ Sensor getLightSensor(SensorManager sensorManager, SensorData sensorData,
+ int fallbackType) {
+ return SensorUtils.findSensor(sensorManager, sensorData, fallbackType);
+ }
+
+ AmbientFilter getAmbientFilter(Resources resources) {
+ return AmbientFilterFactory.createBrightnessFilter(TAG, resources);
+ }
+
+ int getLightSensorRate(Resources resources) {
+ return resources.getInteger(R.integer.config_autoBrightnessLightSensorRate);
+ }
+
+ // should be consistent with SensorEvent.timestamp
+ long getTime() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ interface LightSensorListener {
+ void onAmbientLuxChange(float ambientLux);
+ }
+}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index b43b35b..ddb091d 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -15,6 +15,8 @@
*/
package com.android.server.display.brightness.strategy;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
@@ -133,14 +135,20 @@
// We are still in the process of updating the power state, so there's no need to trigger
// an update again
switchMode(targetDisplayState, /* sendUpdate= */ false);
- final boolean autoBrightnessEnabledInDoze =
- allowAutoBrightnessWhileDozingConfig && Display.isDozeState(targetDisplayState);
+
+ // If the policy is POLICY_DOZE and the display state is STATE_ON, auto-brightness should
+ // only be enabled if the config allows it
+ final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig
+ && policy == POLICY_DOZE && targetDisplayState != Display.STATE_OFF;
+
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
- && (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && ((targetDisplayState == Display.STATE_ON && policy != POLICY_DOZE)
+ || autoBrightnessEnabledInDoze)
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
&& mAutomaticBrightnessController != null;
mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness()
- && !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ && !((targetDisplayState == Display.STATE_ON && policy != POLICY_DOZE)
+ || autoBrightnessEnabledInDoze);
final int autoBrightnessState = mIsAutoBrightnessEnabled
&& brightnessReason != BrightnessReason.REASON_FOLLOWER
? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
index 4d9c18a..c87872c 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
@@ -15,6 +15,8 @@
*/
package com.android.server.display.brightness.strategy;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
@@ -107,14 +109,19 @@
public void setAutoBrightnessState(int targetDisplayState,
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
- final boolean autoBrightnessEnabledInDoze =
- allowAutoBrightnessWhileDozingConfig && Display.isDozeState(targetDisplayState);
+ // If the policy is POLICY_DOZE and the display state is STATE_ON, auto-brightness should
+ // only be enabled if the config allows it
+ final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig
+ && policy == POLICY_DOZE && targetDisplayState != Display.STATE_OFF;
+
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
- && (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && ((targetDisplayState == Display.STATE_ON && policy != POLICY_DOZE)
+ || autoBrightnessEnabledInDoze)
&& brightnessReason != BrightnessReason.REASON_OVERRIDE
&& mAutomaticBrightnessController != null;
mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness()
- && !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ && !((targetDisplayState == Display.STATE_ON && policy != POLICY_DOZE)
+ || autoBrightnessEnabledInDoze);
final int autoBrightnessState = mIsAutoBrightnessEnabled
&& brightnessReason != BrightnessReason.REASON_FOLLOWER
? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index f56d803..41d18cd 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -179,6 +179,11 @@
Flags::offloadDozeOverrideHoldsWakelock
);
+ private final FlagState mOffloadSessionCancelBlockScreenOn =
+ new FlagState(
+ Flags.FLAG_OFFLOAD_SESSION_CANCEL_BLOCK_SCREEN_ON,
+ Flags::offloadSessionCancelBlockScreenOn);
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -352,6 +357,10 @@
return mOffloadDozeOverrideHoldsWakelock.isEnabled();
}
+ public boolean isOffloadSessionCancelBlockScreenOnEnabled() {
+ return mOffloadSessionCancelBlockScreenOn.isEnabled();
+ }
+
/**
* @return Whether to ignore preferredRefreshRate app request conversion to display mode or not
*/
@@ -399,6 +408,7 @@
pw.println(" " + mIgnoreAppPreferredRefreshRate);
pw.println(" " + mSynthetic60hzModes);
pw.println(" " + mOffloadDozeOverrideHoldsWakelock);
+ pw.println(" " + mOffloadSessionCancelBlockScreenOn);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 95d0ca3..1ea5c0b 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -299,3 +299,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "offload_session_cancel_block_screen_on"
+ namespace: "wear_frameworks"
+ description: "Flag for DisplayPowerController to start notifying DisplayOffloadSession about cancelling screen on blocker."
+ bug: "331725519"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
index 9bbcb0c..d387828 100644
--- a/services/core/java/com/android/server/flags/services.aconfig
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -28,3 +28,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "wear_frameworks"
+ name: "optional_background_install_control"
+ description: "Enable BackgroundInstallControl based on system feature to prevent it from starting on form factors."
+ bug: "340928990"
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index cd2c037..5696fba 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -427,7 +427,7 @@
@ServiceThreadOnly
void setHpdSignalType(@Constants.HpdSignalType int signal, int portId) {
assertRunOnServiceThread();
- HdmiLogger.debug("setHpdSignalType: portId %b, signal %b", portId, signal);
+ HdmiLogger.debug("setHpdSignalType: portId %d, signal %d", portId, signal);
mNativeWrapperImpl.nativeSetHpdSignalType(signal, portId);
}
@@ -439,7 +439,7 @@
@Constants.HpdSignalType
int getHpdSignalType(int portId) {
assertRunOnServiceThread();
- HdmiLogger.debug("getHpdSignalType: portId %b ", portId);
+ HdmiLogger.debug("getHpdSignalType: portId %d ", portId);
return mNativeWrapperImpl.nativeGetHpdSignalType(portId);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 6e027c6..bbe7b2b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -144,6 +144,10 @@
private static final String TAG = "HdmiControlService";
private static final Locale HONG_KONG = new Locale("zh", "HK");
private static final Locale MACAU = new Locale("zh", "MO");
+ private static final String TAIWAN_HantLanguageTag = "zh-Hant-TW";
+ private static final String HONG_KONG_HantLanguageTag = "zh-Hant-HK";
+ private static final String HONG_KONG_YUE_HantLanguageTag = "yue-Hant-HK";
+ private static final String MACAU_HantLanguageTag = "zh-Hant-MO";
private static final Map<String, String> sTerminologyToBibliographicMap =
createsTerminologyToBibliographicMap();
@@ -174,7 +178,11 @@
}
@VisibleForTesting static String localeToMenuLanguage(Locale locale) {
- if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
+ if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU) ||
+ locale.toLanguageTag().equals(TAIWAN_HantLanguageTag) ||
+ locale.toLanguageTag().equals(HONG_KONG_HantLanguageTag) ||
+ locale.toLanguageTag().equals(HONG_KONG_YUE_HantLanguageTag) ||
+ locale.toLanguageTag().equals(MACAU_HantLanguageTag)) {
// Android always returns "zho" for all Chinese variants.
// Use "bibliographic" code defined in CEC639-2 for traditional
// Chinese used in Taiwan/Hong Kong/Macau.
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 7d48527..b77f47d 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -69,7 +69,6 @@
@NonNull
private final ImeTargetVisibilityPolicy mImeTargetVisibilityPolicy;
-
DefaultImeVisibilityApplier(InputMethodManagerService service) {
mService = service;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
@@ -80,8 +79,9 @@
@Override
public void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
+ @SoftInputShowHideReason int reason, @UserIdInt int userId) {
+ final var bindingController = mService.getInputMethodBindingController(userId);
+ final IInputMethodInvoker curMethod = bindingController.getCurMethod();
if (curMethod != null) {
if (DEBUG) {
Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken
@@ -99,7 +99,7 @@
mService.mImeBindingState.mFocusedWindowSoftInputMode));
}
mService.onShowHideSoftInputRequested(true /* show */, showInputToken, reason,
- statsToken);
+ statsToken, userId);
}
}
}
@@ -107,8 +107,10 @@
@GuardedBy("ImfLock.class")
@Override
public void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
- final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
+ @UserIdInt int userId) {
+ final var bindingController = mService.getInputMethodBindingController(userId);
+ final IInputMethodInvoker curMethod = bindingController.getCurMethod();
if (curMethod != null) {
// The IME will report its visible state again after the following message finally
// delivered to the IME process as an IPC. Hence the inconsistency between
@@ -130,7 +132,7 @@
mService.mImeBindingState.mFocusedWindowSoftInputMode));
}
mService.onShowHideSoftInputRequested(false /* show */, hideInputToken, reason,
- statsToken);
+ statsToken, userId);
}
}
}
@@ -180,7 +182,7 @@
setImeVisibilityOnFocusedWindowClient(false);
} else {
mService.hideCurrentInputLocked(windowToken, statsToken,
- 0 /* flags */, null /* resultReceiver */, reason);
+ 0 /* flags */, null /* resultReceiver */, reason);
}
break;
case STATE_HIDE_IME_NOT_ALWAYS:
@@ -199,14 +201,14 @@
} else {
mService.showCurrentInputLocked(windowToken, statsToken,
InputMethodManager.SHOW_IMPLICIT, MotionEvent.TOOL_TYPE_UNKNOWN,
- null /* resultReceiver */, reason);
+ null /* resultReceiver */, reason);
}
break;
case STATE_SHOW_IME_SNAPSHOT:
- showImeScreenshot(windowToken, displayIdToShowIme);
+ showImeScreenshot(windowToken, displayIdToShowIme, userId);
break;
case STATE_REMOVE_IME_SNAPSHOT:
- removeImeScreenshot(displayIdToShowIme);
+ removeImeScreenshot(displayIdToShowIme, userId);
break;
default:
throw new IllegalArgumentException("Invalid IME visibility state: " + state);
@@ -215,10 +217,11 @@
@GuardedBy("ImfLock.class")
@Override
- public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
+ public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
+ @UserIdInt int userId) {
if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
- SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */);
+ SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */, userId);
return true;
}
return false;
@@ -226,11 +229,11 @@
@GuardedBy("ImfLock.class")
@Override
- public boolean removeImeScreenshot(int displayId) {
+ public boolean removeImeScreenshot(int displayId, @UserIdInt int userId) {
if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
mService.onShowHideSoftInputRequested(false /* show */,
mService.mImeBindingState.mFocusedWindow,
- REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */);
+ REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */, userId);
return true;
}
return false;
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
index 62adb25..41313fa 100644
--- a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -19,7 +19,6 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -34,24 +33,12 @@
@GuardedBy("ImfLock.class")
private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
- @UserIdInt
- private final int mUserId;
-
- @AnyThread
- @UserIdInt
- int getUserId() {
- return mUserId;
- }
-
- HardwareKeyboardShortcutController(@NonNull InputMethodMap methodMap, @UserIdInt int userId) {
- mUserId = userId;
- reset(methodMap);
+ HardwareKeyboardShortcutController() {
}
@GuardedBy("ImfLock.class")
- void reset(@NonNull InputMethodMap methodMap) {
+ void update(@NonNull InputMethodSettings settings) {
mSubtypeHandles.clear();
- final InputMethodSettings settings = InputMethodSettings.create(methodMap, mUserId);
final List<InputMethodInfo> inputMethods = settings.getEnabledInputMethodList();
for (int i = 0; i < inputMethods.size(); ++i) {
final InputMethodInfo imi = inputMethods.get(i);
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index a5f9b7a..c1069f2 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -33,38 +33,45 @@
/**
* Performs showing IME on top of the given window.
*
- * @param showInputToken A token that represents the requester to show IME.
- * @param statsToken The token tracking the current IME request.
- * @param resultReceiver If non-null, this will be called back to the caller when
- * it has processed request to tell what it has done.
- * @param reason The reason for requesting to show IME.
+ * @param showInputToken a token that represents the requester to show IME
+ * @param statsToken the token tracking the current IME request
+ * @param resultReceiver if non-null, this will be called back to the caller when
+ * it has processed request to tell what it has done
+ * @param reason yhe reason for requesting to show IME
+ * @param userId the target user when performing show IME
*/
default void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {}
+ @SoftInputShowHideReason int reason, @UserIdInt int userId) {
+ }
/**
* Performs hiding IME to the given window
*
- * @param hideInputToken A token that represents the requester to hide IME.
- * @param statsToken The token tracking the current IME request.
- * @param resultReceiver If non-null, this will be called back to the caller when
- * it has processed request to tell what it has done.
- * @param reason The reason for requesting to hide IME.
+ * @param hideInputToken a token that represents the requester to hide IME
+ * @param statsToken the token tracking the current IME request
+ * @param resultReceiver if non-null, this will be called back to the caller when
+ * it has processed request to tell what it has done
+ * @param reason the reason for requesting to hide IME
+ * @param userId the target user when performing hide IME
*/
default void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {}
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
+ @UserIdInt int userId) {
+ }
/**
* Applies the IME visibility from {@link android.inputmethodservice.InputMethodService} with
* according to the given visibility state.
*
- * @param windowToken The token of a window for applying the IME visibility
- * @param statsToken The token tracking the current IME request.
- * @param state The new IME visibility state for the applier to handle
+ * @param windowToken the token of a window for applying the IME visibility
+ * @param statsToken the token tracking the current IME request
+ * @param state the new IME visibility state for the applier to handle
+ * @param userId the target user when applying the IME visibility state
*/
default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {}
+ @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {
+ }
/**
* Updates the IME Z-ordering relative to the given window.
@@ -72,7 +79,7 @@
* This used to adjust the IME relative layer of the window during
* {@link InputMethodManagerService} is in switching IME clients.
*
- * @param windowToken The token of a window to update the Z-ordering relative to the IME.
+ * @param windowToken the token of a window to update the Z-ordering relative to the IME
*/
default void updateImeLayeringByTarget(IBinder windowToken) {
// TODO: add a method in WindowManagerInternal to call DC#updateImeInputAndControlTarget
@@ -82,21 +89,24 @@
/**
* Shows the IME screenshot and attach it to the given IME target window.
*
- * @param windowToken The token of a window to show the IME screenshot.
- * @param displayId The unique id to identify the display
- * @return {@code true} if success, {@code false} otherwise.
+ * @param windowToken the token of a window to show the IME screenshot
+ * @param displayId the unique id to identify the display
+ * @param userId the target user when when showing the IME screenshot
+ * @return {@code true} if success, {@code false} otherwise
*/
- default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId) {
+ default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId,
+ @UserIdInt int userId) {
return false;
}
/**
* Removes the IME screenshot on the given display.
*
- * @param displayId The target display of showing IME screenshot.
- * @return {@code true} if success, {@code false} otherwise.
+ * @param displayId the target display of showing IME screenshot
+ * @param userId the target user of showing IME screenshot
+ * @return {@code true} if success, {@code false} otherwise
*/
- default boolean removeImeScreenshot(int displayId) {
+ default boolean removeImeScreenshot(int displayId, @UserIdInt int userId) {
return false;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3f296ca..a089331 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -377,15 +377,6 @@
@SharedByAllUsersField
private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
- // TODO: Instantiate mSwitchingController for each user.
- @NonNull
- @MultiUserUnawareField
- private InputMethodSubtypeSwitchingController mSwitchingController;
- // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- @NonNull
- @MultiUserUnawareField
- private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
-
@Nullable
private StatusBarManagerInternal mStatusBarManagerInternal;
@SharedByAllUsersField
@@ -1283,7 +1274,13 @@
mShowOngoingImeSwitcherForPhones = false;
- // InputMethodSettingsRepository should be initialized before buildInputMethodListLocked
+ // Executing InputMethodSettingsRepository.initialize() does not mean that it
+ // immediately becomes ready to return the up-to-date InputMethodSettings for each
+ // running user, because we want to return from the constructor as early as possible so
+ // as not to delay the system boot process.
+ // Search for InputMethodSettingsRepository.put() to find where and when it's actually
+ // being updated. In general IMMS should refrain from exposing the existence of IMEs
+ // until systemReady().
InputMethodSettingsRepository.initialize(mHandler, mContext);
AdditionalSubtypeMapRepository.initialize(mHandler, mContext);
@@ -1298,14 +1295,6 @@
getUserData(id);
}
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
-
- mSwitchingController =
- InputMethodSubtypeSwitchingController.createInstanceLocked(context,
- settings.getMethodMap(), settings.getUserId());
- mHardwareKeyboardShortcutController =
- new HardwareKeyboardShortcutController(settings.getMethodMap(),
- settings.getUserId());
mMenuController = new InputMethodMenuController(this);
mVisibilityStateComputer = new ImeVisibilityStateComputer(this);
mVisibilityApplier = new DefaultImeVisibilityApplier(this);
@@ -2936,8 +2925,6 @@
* {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.
* </li>
* <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li>
- * <li>{@link #mSwitchingController} is ignored.</li>
- * <li>{@link #mHardwareKeyboardShortcutController} is ignored.</li>
* <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li>
* <li>and so on.</li>
* </ul>
@@ -2970,6 +2957,10 @@
id = imi.getId();
settings.putSelectedInputMethod(id);
}
+
+ final var userData = getUserData(userId);
+ userData.mSwitchingController.resetCircularListLocked(mContext, settings);
+ userData.mHardwareKeyboardShortcutController.update(settings);
}
@GuardedBy("ImfLock.class")
@@ -3045,20 +3036,9 @@
resetCurrentMethodAndClientLocked(UnbindReason.NO_IME);
}
- // TODO: Instantiate mSwitchingController for each user.
- if (userId == mSwitchingController.getUserId()) {
- mSwitchingController.resetCircularListLocked(settings.getMethodMap());
- } else {
- mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
- mContext, settings.getMethodMap(), userId);
- }
- // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- if (userId == mHardwareKeyboardShortcutController.getUserId()) {
- mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
- } else {
- mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- settings.getMethodMap(), userId);
- }
+ final var userData = getUserData(userId);
+ userData.mSwitchingController.resetCircularListLocked(mContext, settings);
+ userData.mHardwareKeyboardShortcutController.update(settings);
sendOnNavButtonFlagsChangedLocked();
}
@@ -3520,10 +3500,11 @@
mVisibilityStateComputer.requestImeVisibility(windowToken, true);
+ final int userId = mCurrentUserId;
// Ensure binding the connection when IME is going to show.
- final var bindingController = getInputMethodBindingController(mCurrentUserId);
+ final var bindingController = getInputMethodBindingController(userId);
bindingController.setCurrentMethodVisible();
- final IInputMethodInvoker curMethod = getCurMethodLocked();
+ final IInputMethodInvoker curMethod = bindingController.getCurMethod();
ImeTracker.forLogging().onCancelled(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME);
final boolean readyToDispatchToIme;
if (Flags.deferShowSoftInputUntilSessionCreation()) {
@@ -3543,7 +3524,7 @@
}
mVisibilityApplier.performShowIme(windowToken, statsToken,
mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
- resultReceiver, reason);
+ resultReceiver, reason, userId);
mVisibilityStateComputer.setInputShown(true);
return true;
} else {
@@ -3655,7 +3636,9 @@
// since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only
// IMMS#InputShown indicates that the software keyboard is shown.
// TODO(b/246309664): Clean up IMMS#mImeWindowVis
- IInputMethodInvoker curMethod = getCurMethodLocked();
+ final int userId = mCurrentUserId;
+ final var bindingController = getInputMethodBindingController(userId);
+ IInputMethodInvoker curMethod = bindingController.getCurMethod();
final boolean shouldHideSoftInput = curMethod != null
&& (isInputShownLocked() || (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
@@ -3666,11 +3649,11 @@
// IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
// the final state.
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE);
- mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason);
+ mVisibilityApplier.performHideIme(windowToken, statsToken, resultReceiver, reason,
+ userId);
} else {
ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_SERVER_SHOULD_HIDE);
}
- final var bindingController = getInputMethodBindingController(mCurrentUserId);
bindingController.setCurrentMethodNotVisible();
mVisibilityStateComputer.clearImeShowFlags();
// Cancel existing statsToken for show IME as we got a hide request.
@@ -4198,8 +4181,9 @@
final int userId = mCurrentUserId;
final var bindingController = getInputMethodBindingController(userId);
final var currentImi = bindingController.getSelectedMethod();
- final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, currentImi, bindingController.getCurrentSubtype());
+ final ImeSubtypeListItem nextSubtype = getUserData(userId).mSwitchingController
+ .getNextInputMethodLocked(onlyCurrentIme, currentImi,
+ bindingController.getCurrentSubtype());
if (nextSubtype == null) {
return false;
}
@@ -4217,8 +4201,9 @@
final int userId = mCurrentUserId;
final var bindingController = getInputMethodBindingController(userId);
final var currentImi = bindingController.getSelectedMethod();
- final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- false /* onlyCurrentIme */, currentImi, bindingController.getCurrentSubtype());
+ final ImeSubtypeListItem nextSubtype = getUserData(userId).mSwitchingController
+ .getNextInputMethodLocked(false /* onlyCurrentIme */, currentImi,
+ bindingController.getCurrentSubtype());
return nextSubtype != null;
}
}
@@ -4651,13 +4636,11 @@
return;
}
final int userId = mCurrentUserId;
- if (userId != mSwitchingController.getUserId()) {
- return;
- }
- final var imi = getInputMethodBindingController(userId).getSelectedMethod();
+ final var bindingController = getInputMethodBindingController(userId);
+ final InputMethodInfo imi = bindingController.getSelectedMethod();
if (imi != null) {
- mSwitchingController.onUserActionLocked(imi,
- getInputMethodBindingController(userId).getCurrentSubtype());
+ getUserData(userId).mSwitchingController.onUserActionLocked(imi,
+ bindingController.getCurrentSubtype());
}
}
}
@@ -4736,12 +4719,14 @@
*/
@GuardedBy("ImfLock.class")
void onShowHideSoftInputRequested(boolean show, IBinder requestImeToken,
- @SoftInputShowHideReason int reason, @Nullable ImeTracker.Token statsToken) {
+ @SoftInputShowHideReason int reason, @Nullable ImeTracker.Token statsToken,
+ @UserIdInt int userId) {
final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken);
+ final var bindingController = getInputMethodBindingController(userId);
final WindowManagerInternal.ImeTargetInfo info =
mWindowManagerInternal.onToggleImeRequested(
show, mImeBindingState.mFocusedWindow, requestToken,
- getCurTokenDisplayIdLocked());
+ bindingController.getCurTokenDisplayId());
mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo,
info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason,
@@ -4928,7 +4913,7 @@
final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
.getSortedInputMethodAndSubtypeList(
showAuxSubtypes, isScreenLocked, true /* forImeMenu */,
- mContext, settings.getMethodMap(), settings.getUserId());
+ mContext, settings);
if (imList.isEmpty()) {
Slog.w(TAG, "Show switching menu failed, imList is empty,"
+ " showAuxSubtypes: " + showAuxSubtypes
@@ -5316,20 +5301,9 @@
updateDefaultVoiceImeIfNeededLocked();
- // TODO: Instantiate mSwitchingController for each user.
- if (userId == mSwitchingController.getUserId()) {
- mSwitchingController.resetCircularListLocked(settings.getMethodMap());
- } else {
- mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
- mContext, settings.getMethodMap(), mCurrentUserId);
- }
- // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- if (userId == mHardwareKeyboardShortcutController.getUserId()) {
- mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
- } else {
- mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- settings.getMethodMap(), userId);
- }
+ final var userData = getUserData(userId);
+ userData.mSwitchingController.resetCircularListLocked(mContext, settings);
+ userData.mHardwareKeyboardShortcutController.update(settings);
sendOnNavButtonFlagsChangedLocked();
@@ -5640,8 +5614,8 @@
final InputMethodSubtypeHandle currentSubtypeHandle =
InputMethodSubtypeHandle.of(currentImi, bindingController.getCurrentSubtype());
final InputMethodSubtypeHandle nextSubtypeHandle =
- mHardwareKeyboardShortcutController.onSubtypeSwitch(currentSubtypeHandle,
- direction > 0);
+ getUserData(userId).mHardwareKeyboardShortcutController.onSubtypeSwitch(
+ currentSubtypeHandle, direction > 0);
if (nextSubtypeHandle == null) {
return;
}
@@ -6130,6 +6104,8 @@
p.println(" hasMainConnection="
+ u.mBindingController.hasMainConnection());
p.println(" isVisibleBound=" + u.mBindingController.isVisibleBound());
+ p.println(" mSwitchingController:");
+ u.mSwitchingController.dump(p, " ");
};
mUserDataRepository.forAllUserData(userDataDump);
@@ -6150,8 +6126,6 @@
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mStylusIds=" + (mStylusIds != null
? Arrays.toString(mStylusIds.toArray()) : ""));
- p.println(" mSwitchingController:");
- mSwitchingController.dump(p, " ");
p.println(" mStartInputHistory:");
mStartInputHistory.dump(pw, " ");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 770e3b2..bb1b9df 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,10 +16,8 @@
package com.android.server.inputmethod;
-import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.Context;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -48,15 +46,20 @@
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
public static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> {
+
+ @NonNull
public final CharSequence mImeName;
+ @Nullable
public final CharSequence mSubtypeName;
+ @NonNull
public final InputMethodInfo mImi;
public final int mSubtypeId;
public final boolean mIsSystemLocale;
public final boolean mIsSystemLanguage;
- ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName,
- InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) {
+ ImeSubtypeListItem(@NonNull CharSequence imeName, @Nullable CharSequence subtypeName,
+ @NonNull InputMethodInfo imi, int subtypeId, @Nullable String subtypeLocale,
+ @NonNull String systemLocale) {
mImeName = imeName;
mSubtypeName = subtypeName;
mImi = imi;
@@ -69,7 +72,6 @@
if (mIsSystemLocale) {
mIsSystemLanguage = true;
} else {
- // TODO: Use Locale#getLanguage or Locale#toLanguageTag
final String systemLanguage = LocaleUtils.getLanguageFromLocaleString(
systemLocale);
final String subtypeLanguage = LocaleUtils.getLanguageFromLocaleString(
@@ -101,8 +103,9 @@
* <li>{@link #mSubtypeName}</li>
* <li>{@link #mImi} with {@link InputMethodInfo#getId()}</li>
* </ol>
- * Note: this class has a natural ordering that is inconsistent with {@link #equals(Object).
- * This method doesn't compare {@link #mSubtypeId} but {@link #equals(Object)} does.
+ * Note: this class has a natural ordering that is inconsistent with
+ * {@link #equals(Object)}. This method doesn't compare {@link #mSubtypeId} but
+ * {@link #equals(Object)} does.
*
* @param other the object to be compared.
* @return a negative integer, zero, or positive integer as this object is less than, equal
@@ -155,15 +158,15 @@
}
}
+ @NonNull
static List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu,
- @NonNull Context context, @NonNull InputMethodMap methodMap,
- @UserIdInt int userId) {
+ @NonNull Context context, @NonNull InputMethodSettings settings) {
+ final int userId = settings.getUserId();
final Context userAwareContext = context.getUserId() == userId
? context
: context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
final String mSystemLocaleStr = SystemLocaleWrapper.get(userId).get(0).toLanguageTag();
- final InputMethodSettings settings = InputMethodSettings.create(methodMap, userId);
final ArrayList<InputMethodInfo> imis = settings.getEnabledInputMethodList();
if (imis.isEmpty()) {
@@ -190,7 +193,7 @@
enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
}
final CharSequence imeLabel = imi.loadLabel(userAwareContext.getPackageManager());
- if (enabledSubtypeSet.size() > 0) {
+ if (!enabledSubtypeSet.isEmpty()) {
final int subtypeCount = imi.getSubtypeCount();
if (DEBUG) {
Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
@@ -223,14 +226,18 @@
return imList;
}
- private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
+ private static int calculateSubtypeId(@NonNull InputMethodInfo imi,
+ @Nullable InputMethodSubtype subtype) {
return subtype != null ? SubtypeUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode())
: NOT_A_SUBTYPE_ID;
}
private static class StaticRotationList {
+
+ @NonNull
private final List<ImeSubtypeListItem> mImeSubtypeList;
- StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) {
+
+ StaticRotationList(@NonNull List<ImeSubtypeListItem> imeSubtypeList) {
mImeSubtypeList = imeSubtypeList;
}
@@ -242,24 +249,22 @@
* does not have a subtype.
* @return The index in the given list. -1 if not found.
*/
- private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) {
+ private int getIndex(@NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype) {
final int currentSubtypeId = calculateSubtypeId(imi, subtype);
final int numSubtypes = mImeSubtypeList.size();
for (int i = 0; i < numSubtypes; ++i) {
- final ImeSubtypeListItem isli = mImeSubtypeList.get(i);
+ final ImeSubtypeListItem item = mImeSubtypeList.get(i);
// Skip until the current IME/subtype is found.
- if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) {
+ if (imi.equals(item.mImi) && item.mSubtypeId == currentSubtypeId) {
return i;
}
}
return -1;
}
+ @Nullable
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
- InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null) {
- return null;
- }
+ @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype) {
if (mImeSubtypeList.size() <= 1) {
return null;
}
@@ -282,22 +287,24 @@
return null;
}
- protected void dump(final Printer pw, final String prefix) {
+ protected void dump(@NonNull Printer pw, @NonNull String prefix) {
final int numSubtypes = mImeSubtypeList.size();
- for (int i = 0; i < numSubtypes; ++i) {
- final int rank = i;
- final ImeSubtypeListItem item = mImeSubtypeList.get(i);
+ for (int rank = 0; rank < numSubtypes; ++rank) {
+ final ImeSubtypeListItem item = mImeSubtypeList.get(rank);
pw.println(prefix + "rank=" + rank + " item=" + item);
}
}
}
private static class DynamicRotationList {
+
private static final String TAG = DynamicRotationList.class.getSimpleName();
+ @NonNull
private final List<ImeSubtypeListItem> mImeSubtypeList;
+ @NonNull
private final int[] mUsageHistoryOfSubtypeListItemIndex;
- private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
+ private DynamicRotationList(@NonNull List<ImeSubtypeListItem> imeSubtypeListItems) {
mImeSubtypeList = imeSubtypeListItems;
mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()];
final int numSubtypes = mImeSubtypeList.size();
@@ -314,7 +321,8 @@
*
* @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually.
*/
- private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) {
+ private int getUsageRank(@NonNull InputMethodInfo imi,
+ @Nullable InputMethodSubtype subtype) {
final int currentSubtypeId = calculateSubtypeId(imi, subtype);
final int numItems = mUsageHistoryOfSubtypeListItemIndex.length;
for (int usageRank = 0; usageRank < numItems; usageRank++) {
@@ -330,7 +338,8 @@
return -1;
}
- public void onUserAction(InputMethodInfo imi, InputMethodSubtype subtype) {
+ public void onUserAction(@NonNull InputMethodInfo imi,
+ @Nullable InputMethodSubtype subtype) {
final int currentUsageRank = getUsageRank(imi, subtype);
// Do nothing if currentUsageRank == -1 (not found), or currentUsageRank == 0
if (currentUsageRank <= 0) {
@@ -342,8 +351,9 @@
mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex;
}
+ @Nullable
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
- InputMethodInfo imi, InputMethodSubtype subtype) {
+ @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype) {
int currentUsageRank = getUsageRank(imi, subtype);
if (currentUsageRank < 0) {
if (DEBUG) {
@@ -366,7 +376,7 @@
return null;
}
- protected void dump(final Printer pw, final String prefix) {
+ protected void dump(@NonNull Printer pw, @NonNull String prefix) {
for (int rank = 0; rank < mUsageHistoryOfSubtypeListItemIndex.length; ++rank) {
final int index = mUsageHistoryOfSubtypeListItemIndex[rank];
final ImeSubtypeListItem item = mImeSubtypeList.get(index);
@@ -377,58 +387,52 @@
@VisibleForTesting
public static class ControllerImpl {
+
+ @NonNull
private final DynamicRotationList mSwitchingAwareRotationList;
+ @NonNull
private final StaticRotationList mSwitchingUnawareRotationList;
- public static ControllerImpl createFrom(final ControllerImpl currentInstance,
- final List<ImeSubtypeListItem> sortedEnabledItems) {
- DynamicRotationList switchingAwareRotationList = null;
- {
- final List<ImeSubtypeListItem> switchingAwareImeSubtypes =
- filterImeSubtypeList(sortedEnabledItems,
- true /* supportsSwitchingToNextInputMethod */);
- if (currentInstance != null
- && currentInstance.mSwitchingAwareRotationList != null
- && Objects.equals(
- currentInstance.mSwitchingAwareRotationList.mImeSubtypeList,
- switchingAwareImeSubtypes)) {
- // Can reuse the current instance.
- switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList;
- }
- if (switchingAwareRotationList == null) {
- switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
- }
+ @NonNull
+ public static ControllerImpl createFrom(@Nullable ControllerImpl currentInstance,
+ @NonNull List<ImeSubtypeListItem> sortedEnabledItems) {
+ final var switchingAwareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
+ true /* supportsSwitchingToNextInputMethod */);
+ final var switchingUnawareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
+ false /* supportsSwitchingToNextInputMethod */);
+
+ final DynamicRotationList switchingAwareRotationList;
+ if (currentInstance != null && Objects.equals(
+ currentInstance.mSwitchingAwareRotationList.mImeSubtypeList,
+ switchingAwareImeSubtypes)) {
+ // Can reuse the current instance.
+ switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList;
+ } else {
+ switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
}
- StaticRotationList switchingUnawareRotationList = null;
- {
- final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList(
- sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */);
- if (currentInstance != null
- && currentInstance.mSwitchingUnawareRotationList != null
- && Objects.equals(
- currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList,
- switchingUnawareImeSubtypes)) {
- // Can reuse the current instance.
- switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList;
- }
- if (switchingUnawareRotationList == null) {
- switchingUnawareRotationList =
- new StaticRotationList(switchingUnawareImeSubtypes);
- }
+ final StaticRotationList switchingUnawareRotationList;
+ if (currentInstance != null && Objects.equals(
+ currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList,
+ switchingUnawareImeSubtypes)) {
+ // Can reuse the current instance.
+ switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList;
+ } else {
+ switchingUnawareRotationList = new StaticRotationList(switchingUnawareImeSubtypes);
}
return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList);
}
- private ControllerImpl(final DynamicRotationList switchingAwareRotationList,
- final StaticRotationList switchingUnawareRotationList) {
+ private ControllerImpl(@NonNull DynamicRotationList switchingAwareRotationList,
+ @NonNull StaticRotationList switchingUnawareRotationList) {
mSwitchingAwareRotationList = switchingAwareRotationList;
mSwitchingUnawareRotationList = switchingUnawareRotationList;
}
- public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
- InputMethodSubtype subtype) {
+ @Nullable
+ public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme,
+ @Nullable InputMethodInfo imi, @Nullable InputMethodSubtype subtype) {
if (imi == null) {
return null;
}
@@ -441,18 +445,17 @@
}
}
- public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null) {
- return;
- }
+ public void onUserActionLocked(@NonNull InputMethodInfo imi,
+ @Nullable InputMethodSubtype subtype) {
if (imi.supportsSwitchingToNextInputMethod()) {
mSwitchingAwareRotationList.onUserAction(imi, subtype);
}
}
+ @NonNull
private static List<ImeSubtypeListItem> filterImeSubtypeList(
- final List<ImeSubtypeListItem> items,
- final boolean supportsSwitchingToNextInputMethod) {
+ @NonNull List<ImeSubtypeListItem> items,
+ boolean supportsSwitchingToNextInputMethod) {
final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
final int numItems = items.size();
for (int i = 0; i < numItems; i++) {
@@ -473,67 +476,33 @@
}
}
- private final Context mContext;
- @UserIdInt
- private final int mUserId;
+ @NonNull
private ControllerImpl mController;
- private InputMethodSubtypeSwitchingController(@NonNull Context context,
- @NonNull InputMethodMap methodMap, @UserIdInt int userId) {
- mContext = context;
- mUserId = userId;
- mController = ControllerImpl.createFrom(null,
- getSortedInputMethodAndSubtypeList(
- false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
- false /* forImeMenu */, context, methodMap, userId));
+ InputMethodSubtypeSwitchingController() {
+ mController = ControllerImpl.createFrom(null, Collections.emptyList());
}
- @NonNull
- public static InputMethodSubtypeSwitchingController createInstanceLocked(
- @NonNull Context context,
- @NonNull InputMethodMap methodMap, @UserIdInt int userId) {
- return new InputMethodSubtypeSwitchingController(context, methodMap, userId);
- }
-
- @AnyThread
- @UserIdInt
- int getUserId() {
- return mUserId;
- }
-
- public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
- if (mController == null) {
- if (DEBUG) {
- Slog.e(TAG, "mController shouldn't be null.");
- }
- return;
- }
+ public void onUserActionLocked(@NonNull InputMethodInfo imi,
+ @Nullable InputMethodSubtype subtype) {
mController.onUserActionLocked(imi, subtype);
}
- public void resetCircularListLocked(@NonNull InputMethodMap methodMap) {
+ public void resetCircularListLocked(@NonNull Context context,
+ @NonNull InputMethodSettings settings) {
mController = ControllerImpl.createFrom(mController,
getSortedInputMethodAndSubtypeList(
false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
- false /* forImeMenu */, mContext, methodMap, mUserId));
+ false /* forImeMenu */, context, settings));
}
- public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
- InputMethodSubtype subtype) {
- if (mController == null) {
- if (DEBUG) {
- Slog.e(TAG, "mController shouldn't be null.");
- }
- return null;
- }
+ @Nullable
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+ @Nullable InputMethodInfo imi, @Nullable InputMethodSubtype subtype) {
return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
}
public void dump(@NonNull Printer pw, @NonNull String prefix) {
- if (mController != null) {
- mController.dump(pw, prefix);
- } else {
- pw.println(prefix + "mController=null");
- }
+ mController.dump(pw, prefix);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java b/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java
index 559b625..4764e4f 100644
--- a/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java
+++ b/services/core/java/com/android/server/inputmethod/SecureSettingsWrapper.java
@@ -44,6 +44,32 @@
@Nullable
private static volatile ContentResolver sContentResolver = null;
+ private static volatile boolean sTestMode = false;
+
+ /**
+ * Can be called from unit tests to start the test mode, where a fake implementation will be
+ * used instead.
+ *
+ * <p>The fake implementation is just an {@link ArrayMap}. By default it is empty, and the data
+ * written can be read back later.</p>
+ */
+ @AnyThread
+ static void startTestMode() {
+ sTestMode = true;
+ }
+
+ /**
+ * Can be called from unit tests to end the test mode, where a fake implementation will be used
+ * instead.
+ */
+ @AnyThread
+ static void endTestMode() {
+ synchronized (sUserMap) {
+ sUserMap.clear();
+ }
+ sTestMode = false;
+ }
+
/**
* Not intended to be instantiated.
*/
@@ -78,6 +104,52 @@
int getInt(String key, int defaultValue);
}
+ private static class FakeReaderWriterImpl implements ReaderWriter {
+ @GuardedBy("mNonPersistentKeyValues")
+ private final ArrayMap<String, String> mNonPersistentKeyValues = new ArrayMap<>();
+
+ @AnyThread
+ @Override
+ public void putString(String key, String value) {
+ synchronized (mNonPersistentKeyValues) {
+ mNonPersistentKeyValues.put(key, value);
+ }
+ }
+
+ @AnyThread
+ @Nullable
+ @Override
+ public String getString(String key, String defaultValue) {
+ synchronized (mNonPersistentKeyValues) {
+ if (mNonPersistentKeyValues.containsKey(key)) {
+ final String result = mNonPersistentKeyValues.get(key);
+ return result != null ? result : defaultValue;
+ }
+ return defaultValue;
+ }
+ }
+
+ @AnyThread
+ @Override
+ public void putInt(String key, int value) {
+ synchronized (mNonPersistentKeyValues) {
+ mNonPersistentKeyValues.put(key, String.valueOf(value));
+ }
+ }
+
+ @AnyThread
+ @Override
+ public int getInt(String key, int defaultValue) {
+ synchronized (mNonPersistentKeyValues) {
+ if (mNonPersistentKeyValues.containsKey(key)) {
+ final String result = mNonPersistentKeyValues.get(key);
+ return result != null ? Integer.parseInt(result) : defaultValue;
+ }
+ return defaultValue;
+ }
+ }
+ }
+
private static class UnlockedUserImpl implements ReaderWriter {
@UserIdInt
private final int mUserId;
@@ -200,6 +272,9 @@
private static ReaderWriter createImpl(@NonNull UserManagerInternal userManagerInternal,
@UserIdInt int userId) {
+ if (sTestMode) {
+ return new FakeReaderWriterImpl();
+ }
return userManagerInternal.isUserUnlockingOrUnlocked(userId)
? new UnlockedUserImpl(userId, sContentResolver)
: new LockedUserImpl(userId, sContentResolver);
@@ -234,6 +309,9 @@
return readerWriter;
}
}
+ if (sTestMode) {
+ return putOrGet(userId, new FakeReaderWriterImpl());
+ }
final UserManagerInternal userManagerInternal =
LocalServices.getService(UserManagerInternal.class);
if (!userManagerInternal.exists(userId)) {
@@ -276,6 +354,10 @@
*/
@AnyThread
static void onUserStarting(@UserIdInt int userId) {
+ if (sTestMode) {
+ putOrGet(userId, new FakeReaderWriterImpl());
+ return;
+ }
putOrGet(userId, createImpl(LocalServices.getService(UserManagerInternal.class), userId));
}
@@ -286,6 +368,10 @@
*/
@AnyThread
static void onUserUnlocking(@UserIdInt int userId) {
+ if (sTestMode) {
+ putOrGet(userId, new FakeReaderWriterImpl());
+ return;
+ }
final ReaderWriter readerWriter = new UnlockedUserImpl(userId, sContentResolver);
putOrGet(userId, readerWriter);
}
diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
index 2b19d3e..3da4a14 100644
--- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java
+++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
@@ -53,7 +53,8 @@
}
}
- UserDataRepository(@NonNull Handler handler, @NonNull UserManagerInternal userManagerInternal,
+ UserDataRepository(
+ @NonNull Handler handler, @NonNull UserManagerInternal userManagerInternal,
@NonNull IntFunction<InputMethodBindingController> bindingControllerFactory) {
mBindingControllerFactory = bindingControllerFactory;
userManagerInternal.addUserLifecycleListener(
@@ -88,6 +89,12 @@
@NonNull
final InputMethodBindingController mBindingController;
+ @NonNull
+ final InputMethodSubtypeSwitchingController mSwitchingController;
+
+ @NonNull
+ final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
+
/**
* Intended to be instantiated only from this file.
*/
@@ -95,6 +102,8 @@
@NonNull InputMethodBindingController bindingController) {
mUserId = userId;
mBindingController = bindingController;
+ mSwitchingController = new InputMethodSubtypeSwitchingController();
+ mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController();
}
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 363a4a7..91a4d6f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -1126,7 +1126,7 @@
}
}
- private void sendHostEndpointConnectedEvent() {
+ void sendHostEndpointConnectedEvent() {
HostEndpointInfo info = new HostEndpointInfo();
info.hostEndpointId = (char) mHostEndPointId;
info.packageName = mPackage;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index b3fb147..381b667 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -74,13 +74,13 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.PriorityQueue;
-import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -158,13 +158,11 @@
// A queue of reliable message records for duplicate detection
private final PriorityQueue<ReliableMessageRecord> mReliableMessageRecordQueue =
- new PriorityQueue<ReliableMessageRecord>(
- (ReliableMessageRecord left, ReliableMessageRecord right) -> {
- return Long.compare(left.getTimestamp(), right.getTimestamp());
- });
+ new PriorityQueue<>(
+ Comparator.comparingLong(ReliableMessageRecord::getTimestamp));
- // The test mode manager that manages behaviors during test mode.
- private final TestModeManager mTestModeManager = new TestModeManager();
+ // The test mode manager that manages behaviors during test mode
+ private final ContextHubTestModeManager mTestModeManager = new ContextHubTestModeManager();
// The period of the recurring time
private static final int PERIOD_METRIC_QUERY_DAYS = 1;
@@ -179,10 +177,10 @@
private boolean mIsBtMainEnabled = false;
// True if test mode is enabled for the Context Hub
- private AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false);
+ private final AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false);
// A hashmap used to record if a contexthub is waiting for daily query
- private Set<Integer> mMetricQueryPendingContextHubIds =
+ private final Set<Integer> mMetricQueryPendingContextHubIds =
Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
// Lock object for sendWifiSettingUpdate()
@@ -230,9 +228,11 @@
if (Flags.reliableMessageImplementation()
&& Flags.reliableMessageTestModeBehavior()
&& mIsTestModeEnabled.get()
- && mTestModeManager.handleNanoappMessage(mContextHubId, hostEndpointId,
- message, nanoappPermissions, messagePermissions)) {
- // The TestModeManager handled the nanoapp message, so return here.
+ && mTestModeManager.handleNanoappMessage(() -> {
+ handleClientMessageCallback(mContextHubId, hostEndpointId, message,
+ nanoappPermissions, messagePermissions);
+ }, message)) {
+ // The ContextHubTestModeManager handled the nanoapp message, so return here.
return;
}
@@ -242,10 +242,14 @@
@Override
public void handleServiceRestart() {
- Log.i(TAG, "Starting Context Hub Service restart");
+ Log.i(TAG, "Recovering from Context Hub HAL restart...");
initExistingCallbacks();
resetSettings();
- Log.i(TAG, "Finished Context Hub Service restart");
+ if (Flags.reconnectHostEndpointsAfterHalRestart()) {
+ mClientManager.forEachClientOfHub(mContextHubId,
+ ContextHubClientBroker::sendHostEndpointConnectedEvent);
+ }
+ Log.i(TAG, "Finished recovering from Context Hub HAL restart");
}
@Override
@@ -258,8 +262,6 @@
* Records a reliable message from a nanoapp for duplicate detection.
*/
private static class ReliableMessageRecord {
- public static final int TIMEOUT_NS = 1000000000;
-
public int mContextHubId;
public long mTimestamp;
public int mMessageSequenceNumber;
@@ -294,65 +296,8 @@
}
public boolean isExpired() {
- return mTimestamp + TIMEOUT_NS < SystemClock.elapsedRealtimeNanos();
- }
- }
-
- /**
- * A class to manage behaviors during test mode. This is used for testing.
- */
- private class TestModeManager {
- /**
- * Probability (in percent) of duplicating a message.
- */
- private static final int MESSAGE_DUPLICATION_PROBABILITY_PERCENT = 50;
-
- /**
- * The number of total messages to send when the duplicate event happens.
- */
- private static final int NUM_MESSAGES_TO_DUPLICATE = 3;
-
- /**
- * A probability percent for a certain event.
- */
- private static final int MAX_PROBABILITY_PERCENT = 100;
-
- private Random mRandom = new Random();
-
- /**
- * @see ContextHubServiceCallback.handleNanoappMessage
- * @return whether the message was handled
- */
- public boolean handleNanoappMessage(int contextHubId,
- short hostEndpointId, NanoAppMessage message,
- List<String> nanoappPermissions, List<String> messagePermissions) {
- if (!message.isReliable()) {
- return false;
- }
-
- if (Flags.reliableMessageDuplicateDetectionService()
- && didEventHappen(MESSAGE_DUPLICATION_PROBABILITY_PERCENT)) {
- Log.i(TAG, "[TEST MODE] Duplicating message ("
- + NUM_MESSAGES_TO_DUPLICATE
- + " sends) with message sequence number: "
- + message.getMessageSequenceNumber());
- for (int i = 0; i < NUM_MESSAGES_TO_DUPLICATE; ++i) {
- handleClientMessageCallback(contextHubId, hostEndpointId,
- message, nanoappPermissions, messagePermissions);
- }
- return true;
- }
- return false;
- }
-
- /**
- * Returns true if the event with percentPercent did happen.
- *
- * @param probabilityPercent the percent probability of the event.
- * @return true if the event happened, false otherwise.
- */
- private boolean didEventHappen(int probabilityPercent) {
- return mRandom.nextInt(MAX_PROBABILITY_PERCENT) < probabilityPercent;
+ return mTimestamp + ContextHubTransactionManager.RELIABLE_MESSAGE_TIMEOUT.toNanos()
+ < SystemClock.elapsedRealtimeNanos();
}
}
@@ -476,7 +421,7 @@
hubInfo = mContextHubWrapper.getHubs();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while getting Context Hub info", e);
- hubInfo = new Pair(Collections.emptyList(), Collections.emptyList());
+ hubInfo = new Pair<>(Collections.emptyList(), Collections.emptyList());
}
long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
@@ -536,6 +481,7 @@
for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
try {
mContextHubWrapper.registerExistingCallback(contextHubId);
+ Log.i(TAG, "Re-registered callback to context hub " + contextHubId);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while registering existing service callback for hub "
+ "(ID = " + contextHubId + ")", e);
@@ -647,7 +593,7 @@
mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
// If we are in HSUM mode, any user can change the microphone setting
- if (mUserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) {
+ if (UserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) {
Log.d(TAG, "User: " + userId + " mic privacy: " + enabled);
sendMicrophoneDisableSettingUpdate(enabled);
}
@@ -720,33 +666,30 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public int[] getContextHubHandles() throws RemoteException {
+ public int[] getContextHubHandles() {
super.getContextHubHandles_enforcePermission();
-
return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet());
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
+ public ContextHubInfo getContextHubInfo(int contextHubHandle) {
super.getContextHubInfo_enforcePermission();
-
if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) {
Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo");
return null;
}
-
return mContextHubIdToInfoMap.get(contextHubHandle);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Returns a List of ContextHubInfo object describing the available hubs.
*
* @return the List of ContextHubInfo objects
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public List<ContextHubInfo> getContextHubs() throws RemoteException {
+ public List<ContextHubInfo> getContextHubs() {
super.getContextHubs_enforcePermission();
return mContextHubInfoList;
@@ -814,7 +757,7 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException {
+ public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) {
super.loadNanoApp_enforcePermission();
if (mContextHubWrapper == null) {
@@ -843,7 +786,7 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public int unloadNanoApp(int nanoAppHandle) throws RemoteException {
+ public int unloadNanoApp(int nanoAppHandle) {
super.unloadNanoApp_enforcePermission();
if (mContextHubWrapper == null) {
@@ -870,7 +813,7 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException {
+ public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
super.getNanoAppInstanceInfo_enforcePermission();
@@ -880,7 +823,7 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public int[] findNanoAppOnHub(
- int contextHubHandle, NanoAppFilter filter) throws RemoteException {
+ int contextHubHandle, NanoAppFilter filter) {
super.findNanoAppOnHub_enforcePermission();
@@ -895,20 +838,19 @@
int[] retArray = new int[foundInstances.size()];
for (int i = 0; i < foundInstances.size(); i++) {
- retArray[i] = foundInstances.get(i).intValue();
+ retArray[i] = foundInstances.get(i);
}
return retArray;
}
/**
* Performs a query at the specified hub.
- * <p>
- * This method should only be invoked internally by the service, either to update the service
+ *
+ * <p>This method should only be invoked internally by the service, either to update the service
* cache or as a result of an explicit query requested by a client through the sendMessage API.
*
* @param contextHubId the ID of the hub to do the query
* @return true if the query succeeded
- * @throws IllegalStateException if the transaction queue is full
*/
private boolean queryNanoAppsInternal(int contextHubId) {
if (mContextHubWrapper == null) {
@@ -1003,7 +945,7 @@
return;
}
- byte errorCode = ErrorCode.OK;
+ byte errorCode;
synchronized (mReliableMessageRecordQueue) {
Optional<ReliableMessageRecord> record =
findReliableMessageRecord(contextHubId,
@@ -1219,7 +1161,6 @@
return mContextHubIdToInfoMap.containsKey(contextHubId);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Creates and registers a client at the service for the specified Context Hub.
*
@@ -1232,10 +1173,11 @@
* @throws IllegalStateException if max number of clients have already registered
* @throws NullPointerException if clientCallback is null
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public IContextHubClient createClient(
int contextHubId, IContextHubClientCallback clientCallback,
- @Nullable String attributionTag, String packageName) throws RemoteException {
+ @Nullable String attributionTag, String packageName) {
super.createClient_enforcePermission();
if (!isValidContextHubId(contextHubId)) {
@@ -1250,7 +1192,6 @@
contextHubInfo, clientCallback, attributionTag, mTransactionManager, packageName);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Creates and registers a PendingIntent client at the service for the specified Context Hub.
*
@@ -1262,10 +1203,11 @@
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public IContextHubClient createPendingIntentClient(
int contextHubId, PendingIntent pendingIntent, long nanoAppId,
- @Nullable String attributionTag) throws RemoteException {
+ @Nullable String attributionTag) {
super.createPendingIntentClient_enforcePermission();
if (!isValidContextHubId(contextHubId)) {
@@ -1277,15 +1219,14 @@
contextHubInfo, pendingIntent, nanoAppId, attributionTag, mTransactionManager);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Loads a nanoapp binary at the specified Context hub.
*
* @param contextHubId the ID of the hub to load the binary
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppBinary the binary to load
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void loadNanoAppOnHub(
int contextHubId, IContextHubTransactionCallback transactionCallback,
@@ -1308,15 +1249,14 @@
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Unloads a nanoapp from the specified Context Hub.
*
* @param contextHubId the ID of the hub to unload the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to unload
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void unloadNanoAppFromHub(
int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
@@ -1333,19 +1273,17 @@
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Enables a nanoapp at the specified Context Hub.
*
* @param contextHubId the ID of the hub to enable the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to enable
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void enableNanoApp(
- int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
- throws RemoteException {
+ int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) {
super.enableNanoApp_enforcePermission();
if (!checkHalProxyAndContextHubId(
@@ -1358,19 +1296,17 @@
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Disables a nanoapp at the specified Context Hub.
*
* @param contextHubId the ID of the hub to disable the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to disable
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public void disableNanoApp(
- int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
- throws RemoteException {
+ int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) {
super.disableNanoApp_enforcePermission();
if (!checkHalProxyAndContextHubId(
@@ -1383,17 +1319,16 @@
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Queries for a list of nanoapps from the specified Context hub.
*
* @param contextHubId the ID of the hub to query
* @param transactionCallback the client-facing transaction callback interface
- * @throws IllegalStateException if the transaction queue is full
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback)
- throws RemoteException {
+ public void queryNanoApps(int contextHubId,
+ IContextHubTransactionCallback transactionCallback) {
super.queryNanoApps_enforcePermission();
if (!checkHalProxyAndContextHubId(
@@ -1406,16 +1341,15 @@
mTransactionManager.addTransaction(transaction);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Queries for a list of preloaded nanoapp IDs from the specified Context Hub.
*
* @param hubInfo The Context Hub to query a list of nanoapps from.
* @return The list of 64-bit IDs of the preloaded nanoapps.
- * @throws NullPointerException if hubInfo is null
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
- public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException {
+ public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) {
super.getPreloadedNanoAppIds_enforcePermission();
Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
@@ -1426,7 +1360,6 @@
return nanoappIds;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
/**
* Puts the context hub in and out of test mode. Test mode is a clean state
* where tests can be executed in the same environment. If enable is true,
@@ -1442,6 +1375,7 @@
* test mode.
* @return If true, the operation was successful; false otherwise.
*/
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Override
public boolean setTestMode(boolean enable) {
super.setTestMode_enforcePermission();
@@ -1551,10 +1485,6 @@
}
}
- private void checkPermissions() {
- ContextHubServiceUtil.checkPermissions(mContext);
- }
-
private int onMessageReceiptOldApi(
int msgType, int contextHubHandle, int appInstance, byte[] data) {
if (data == null) {
@@ -1586,7 +1516,6 @@
callback.onMessageReceipt(contextHubHandle, appInstance, msg);
} catch (RemoteException e) {
Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
- continue;
}
}
mCallbacksList.finishBroadcast();
@@ -1729,8 +1658,8 @@
* Hub.
*/
private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
- boolean isEnabled = mSensorPrivacyManagerInternal == null ? false :
- mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
+ boolean isEnabled = mSensorPrivacyManagerInternal != null
+ && mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE);
sendMicrophoneDisableSettingUpdate(isEnabled);
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
index 6da7a65..2ec9bdb 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceTransaction.java
@@ -27,53 +27,65 @@
*
* @hide
*/
-/* package */ abstract class ContextHubServiceTransaction {
+abstract class ContextHubServiceTransaction {
private final int mTransactionId;
+
@ContextHubTransaction.Type
private final int mTransactionType;
- /** The ID of the nanoapp this transaction is targeted for, null if not applicable. */
private final Long mNanoAppId;
- /**
- * The host package associated with this transaction.
- */
private final String mPackage;
- /**
- * The message sequence number associated with this transaction, null if not applicable.
- */
private final Integer mMessageSequenceNumber;
- /**
- * true if the transaction has already completed, false otherwise
- */
+ private long mNextRetryTime;
+
+ private long mTimeoutTime;
+
+ /** The number of times the transaction has been started (start function called). */
+ private int mNumCompletedStartCalls;
+
+ private final short mHostEndpointId;
+
private boolean mIsComplete = false;
- /* package */ ContextHubServiceTransaction(int id, int type, String packageName) {
+ ContextHubServiceTransaction(int id, int type, String packageName) {
mTransactionId = id;
mTransactionType = type;
mNanoAppId = null;
mPackage = packageName;
mMessageSequenceNumber = null;
+ mNextRetryTime = Long.MAX_VALUE;
+ mTimeoutTime = Long.MAX_VALUE;
+ mNumCompletedStartCalls = 0;
+ mHostEndpointId = Short.MAX_VALUE;
}
- /* package */ ContextHubServiceTransaction(int id, int type, long nanoAppId,
+ ContextHubServiceTransaction(int id, int type, long nanoAppId,
String packageName) {
mTransactionId = id;
mTransactionType = type;
mNanoAppId = nanoAppId;
mPackage = packageName;
mMessageSequenceNumber = null;
+ mNextRetryTime = Long.MAX_VALUE;
+ mTimeoutTime = Long.MAX_VALUE;
+ mNumCompletedStartCalls = 0;
+ mHostEndpointId = Short.MAX_VALUE;
}
- /* package */ ContextHubServiceTransaction(int id, int type, String packageName,
- int messageSequenceNumber) {
+ ContextHubServiceTransaction(int id, int type, String packageName,
+ int messageSequenceNumber, short hostEndpointId) {
mTransactionId = id;
mTransactionType = type;
mNanoAppId = null;
mPackage = packageName;
mMessageSequenceNumber = messageSequenceNumber;
+ mNextRetryTime = Long.MAX_VALUE;
+ mTimeoutTime = Long.MAX_VALUE;
+ mNumCompletedStartCalls = 0;
+ mHostEndpointId = hostEndpointId;
}
/**
@@ -95,7 +107,7 @@
*
* @param result the result of the transaction
*/
- /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ void onTransactionComplete(@ContextHubTransaction.Result int result) {
}
/**
@@ -106,44 +118,51 @@
* @param result the result of the query
* @param nanoAppStateList the list of nanoapps given by the query response
*/
- /* package */ void onQueryResponse(
+ void onQueryResponse(
@ContextHubTransaction.Result int result, List<NanoAppState> nanoAppStateList) {
}
- /**
- * @return the ID of this transaction
- */
- /* package */ int getTransactionId() {
+ int getTransactionId() {
return mTransactionId;
}
- /**
- * @return the type of this transaction
- * @see ContextHubTransaction.Type
- */
@ContextHubTransaction.Type
- /* package */ int getTransactionType() {
+ int getTransactionType() {
return mTransactionType;
}
- /**
- * @return the message sequence number of this transaction
- */
Integer getMessageSequenceNumber() {
return mMessageSequenceNumber;
}
+ long getNextRetryTime() {
+ return mNextRetryTime;
+ }
+
+ long getTimeoutTime() {
+ return mTimeoutTime;
+ }
+
+ int getNumCompletedStartCalls() {
+ return mNumCompletedStartCalls;
+ }
+
+ short getHostEndpointId() {
+ return mHostEndpointId;
+ }
+
/**
* Gets the timeout period as defined in IContexthub.hal
*
* @return the timeout of this transaction in the specified time unit
*/
- /* package */ long getTimeout(TimeUnit unit) {
+ long getTimeout(TimeUnit unit) {
switch (mTransactionType) {
case ContextHubTransaction.TYPE_LOAD_NANOAPP:
return unit.convert(30L, TimeUnit.SECONDS);
case ContextHubTransaction.TYPE_RELIABLE_MESSAGE:
- return unit.convert(1000L, TimeUnit.MILLISECONDS);
+ return unit.convert(ContextHubTransactionManager.RELIABLE_MESSAGE_TIMEOUT.toNanos(),
+ TimeUnit.NANOSECONDS);
case ContextHubTransaction.TYPE_UNLOAD_NANOAPP:
case ContextHubTransaction.TYPE_ENABLE_NANOAPP:
case ContextHubTransaction.TYPE_DISABLE_NANOAPP:
@@ -159,14 +178,23 @@
*
* Should only be called as a result of a response from a Context Hub callback
*/
- /* package */ void setComplete() {
+ void setComplete() {
mIsComplete = true;
}
- /**
- * @return true if the transaction has already completed, false otherwise
- */
- /* package */ boolean isComplete() {
+ void setNextRetryTime(long nextRetryTime) {
+ mNextRetryTime = nextRetryTime;
+ }
+
+ void setTimeoutTime(long timeoutTime) {
+ mTimeoutTime = timeoutTime;
+ }
+
+ void setNumCompletedStartCalls(int numCompletedStartCalls) {
+ mNumCompletedStartCalls = numCompletedStartCalls;
+ }
+
+ boolean isComplete() {
return mIsComplete;
}
@@ -187,7 +215,18 @@
out.append(", messageSequenceNumber = ");
out.append(mMessageSequenceNumber);
}
+ if (mTransactionType == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+ out.append(", nextRetryTime = ");
+ out.append(mNextRetryTime);
+ out.append(", timeoutTime = ");
+ out.append(mTimeoutTime);
+ out.append(", numCompletedStartCalls = ");
+ out.append(mNumCompletedStartCalls);
+ out.append(", hostEndpointId = ");
+ out.append(mHostEndpointId);
+ }
out.append(")");
+
return out.toString();
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTestModeManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTestModeManager.java
new file mode 100644
index 0000000..e50324e
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTestModeManager.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import android.chre.flags.Flags;
+import android.hardware.location.NanoAppMessage;
+import android.util.Log;
+
+import java.util.Random;
+
+/**
+ * A class to manage behaviors during test mode. This is used for testing.
+ * @hide
+ */
+public class ContextHubTestModeManager {
+ private static final String TAG = "ContextHubTestModeManager";
+
+ /** Probability (in percent) of duplicating a message. */
+ private static final int MESSAGE_DROP_PROBABILITY_PERCENT = 20;
+
+ /** Probability (in percent) of duplicating a message. */
+ private static final int MESSAGE_DUPLICATION_PROBABILITY_PERCENT = 20;
+
+ /** The number of total messages to send when the duplicate event happens. */
+ private static final int NUM_MESSAGES_TO_DUPLICATE = 3;
+
+ /** A probability percent for a certain event. */
+ private static final int MAX_PROBABILITY_PERCENT = 100;
+
+ private final Random mRandom = new Random();
+
+ /**
+ * @return whether the message was handled
+ * @see ContextHubServiceCallback#handleNanoappMessage
+ */
+ public boolean handleNanoappMessage(Runnable handleMessage, NanoAppMessage message) {
+ if (Flags.reliableMessageDuplicateDetectionService()
+ && message.isReliable()
+ && mRandom.nextInt(MAX_PROBABILITY_PERCENT)
+ < MESSAGE_DUPLICATION_PROBABILITY_PERCENT) {
+ Log.i(TAG, "[TEST MODE] Duplicating message ("
+ + NUM_MESSAGES_TO_DUPLICATE
+ + " sends) with message sequence number: "
+ + message.getMessageSequenceNumber());
+ for (int i = 0; i < NUM_MESSAGES_TO_DUPLICATE; ++i) {
+ handleMessage.run();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return whether the message was handled
+ * @see IContextHubWrapper#sendMessageToContextHub
+ */
+ public boolean sendMessageToContextHub(NanoAppMessage message) {
+ if (Flags.reliableMessageRetrySupportService()
+ && message.isReliable()
+ && mRandom.nextInt(MAX_PROBABILITY_PERCENT)
+ < MESSAGE_DROP_PROBABILITY_PERCENT) {
+ Log.i(TAG, "[TEST MODE] Dropping message with message sequence number: "
+ + message.getMessageSequenceNumber());
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index ec94e2b..3051379 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -16,19 +16,26 @@
package com.android.server.location.contexthub;
+import android.chre.flags.Flags;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubTransactionCallback;
import android.hardware.location.NanoAppBinary;
import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
+import java.time.Duration;
import java.util.ArrayDeque;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Random;
+import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -47,34 +54,30 @@
/* package */ class ContextHubTransactionManager {
private static final String TAG = "ContextHubTransactionManager";
- /*
- * Maximum number of transaction requests that can be pending at a time
- */
+ public static final Duration RELIABLE_MESSAGE_TIMEOUT = Duration.ofSeconds(1);
+
private static final int MAX_PENDING_REQUESTS = 10000;
- /*
- * The proxy to talk to the Context Hub
- */
+ private static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
+
+ private static final Duration RELIABLE_MESSAGE_RETRY_WAIT_TIME = Duration.ofMillis(250);
+
+ private static final Duration RELIABLE_MESSAGE_MIN_WAIT_TIME = Duration.ofNanos(1000);
+
private final IContextHubWrapper mContextHubProxy;
- /*
- * The manager for all clients for the service.
- */
private final ContextHubClientManager mClientManager;
- /*
- * The nanoapp state manager for the service
- */
private final NanoAppStateManager mNanoAppStateManager;
- /*
- * A queue containing the current transactions
- */
private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
- /*
- * The next available transaction ID
- */
+ private final Map<Integer, ContextHubServiceTransaction> mReliableMessageTransactionMap =
+ new HashMap<>();
+
+ /** A set of host endpoint IDs that have an active pending transaction. */
+ private final Set<Short> mReliableMessageHostEndpointIdActiveSet = new HashSet<>();
+
private final AtomicInteger mNextAvailableId = new AtomicInteger();
/**
@@ -86,10 +89,12 @@
new AtomicInteger(new Random().nextInt(Integer.MAX_VALUE / 2));
/*
- * An executor and the future object for scheduling timeout timers
+ * An executor and the future object for scheduling timeout timers and
+ * for scheduling the processing of reliable message transactions.
*/
- private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
+ private final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(1);
private ScheduledFuture<?> mTimeoutFuture = null;
+ private ScheduledFuture<?> mReliableMessageTransactionFuture = null;
/*
* The list of previous transaction records.
@@ -333,7 +338,7 @@
IContextHubTransactionCallback transactionCallback, String packageName) {
return new ContextHubServiceTransaction(mNextAvailableId.getAndIncrement(),
ContextHubTransaction.TYPE_RELIABLE_MESSAGE, packageName,
- mNextAvailableMessageSequenceNumber.getAndIncrement()) {
+ mNextAvailableMessageSequenceNumber.getAndIncrement(), hostEndpointId) {
@Override
/* package */ int onTransact() {
try {
@@ -416,16 +421,23 @@
return;
}
- if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
+ if (mTransactionQueue.size() >= MAX_PENDING_REQUESTS
+ || mReliableMessageTransactionMap.size() >= MAX_PENDING_REQUESTS) {
throw new IllegalStateException("Transaction queue is full (capacity = "
+ MAX_PENDING_REQUESTS + ")");
}
- mTransactionQueue.add(transaction);
mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
-
- if (mTransactionQueue.size() == 1) {
- startNextTransaction();
+ if (Flags.reliableMessageRetrySupportService()
+ && transaction.getTransactionType()
+ == ContextHubTransaction.TYPE_RELIABLE_MESSAGE) {
+ mReliableMessageTransactionMap.put(transaction.getMessageSequenceNumber(), transaction);
+ mExecutor.execute(() -> processMessageTransactions());
+ } else {
+ mTransactionQueue.add(transaction);
+ if (mTransactionQueue.size() == 1) {
+ startNextTransaction();
+ }
}
}
@@ -455,26 +467,42 @@
/* package */
synchronized void onMessageDeliveryResponse(int messageSequenceNumber, boolean success) {
- ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (!Flags.reliableMessageRetrySupportService()) {
+ ContextHubServiceTransaction transaction = mTransactionQueue.peek();
+ if (transaction == null) {
+ Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+ return;
+ }
+
+ Integer transactionMessageSequenceNumber = transaction.getMessageSequenceNumber();
+ if (transaction.getTransactionType() != ContextHubTransaction.TYPE_RELIABLE_MESSAGE
+ || transactionMessageSequenceNumber == null
+ || transactionMessageSequenceNumber != messageSequenceNumber) {
+ Log.w(TAG, "Received unexpected message transaction response (expected message "
+ + "sequence number = "
+ + transaction.getMessageSequenceNumber()
+ + ", received messageSequenceNumber = " + messageSequenceNumber + ")");
+ return;
+ }
+
+ transaction.onTransactionComplete(success ? ContextHubTransaction.RESULT_SUCCESS :
+ ContextHubTransaction.RESULT_FAILED_AT_HUB);
+ removeTransactionAndStartNext();
+ return;
+ }
+
+ ContextHubServiceTransaction transaction =
+ mReliableMessageTransactionMap.get(messageSequenceNumber);
if (transaction == null) {
- Log.w(TAG, "Received unexpected transaction response (no transaction pending)");
+ Log.w(TAG, "Could not find reliable message transaction with message sequence number"
+ + messageSequenceNumber);
return;
}
- Integer transactionMessageSequenceNumber = transaction.getMessageSequenceNumber();
- if (transaction.getTransactionType() != ContextHubTransaction.TYPE_RELIABLE_MESSAGE
- || transactionMessageSequenceNumber == null
- || transactionMessageSequenceNumber != messageSequenceNumber) {
- Log.w(TAG, "Received unexpected message transaction response (expected message "
- + "sequence number = "
- + transaction.getMessageSequenceNumber()
- + ", received messageSequenceNumber = " + messageSequenceNumber + ")");
- return;
- }
-
- transaction.onTransactionComplete(success ? ContextHubTransaction.RESULT_SUCCESS :
- ContextHubTransaction.RESULT_FAILED_AT_HUB);
- removeTransactionAndStartNext();
+ completeMessageTransaction(transaction,
+ success ? ContextHubTransaction.RESULT_SUCCESS
+ : ContextHubTransaction.RESULT_FAILED_AT_HUB);
+ mExecutor.execute(() -> processMessageTransactions());
}
/**
@@ -503,6 +531,15 @@
*/
/* package */
synchronized void onHubReset() {
+ if (Flags.reliableMessageRetrySupportService()) {
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+ mReliableMessageTransactionMap.entrySet().iterator();
+ while (iter.hasNext()) {
+ completeMessageTransaction(iter.next().getValue(),
+ ContextHubTransaction.RESULT_FAILED_AT_HUB, iter);
+ }
+ }
+
ContextHubServiceTransaction transaction = mTransactionQueue.peek();
if (transaction == null) {
return;
@@ -566,7 +603,7 @@
long timeoutMs = transaction.getTimeout(TimeUnit.MILLISECONDS);
try {
- mTimeoutFuture = mTimeoutExecutor.schedule(
+ mTimeoutFuture = mExecutor.schedule(
onTimeoutFunc, timeoutMs, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Log.e(TAG, "Error when schedule a timer", e);
@@ -579,6 +616,136 @@
}
}
+ /**
+ * Processes message transactions, starting and completing them as needed.
+ * This function is called when adding a message transaction or when a timer
+ * expires for an existing message transaction's retry or timeout. The
+ * internal processing loop will iterate at most twice as if one iteration
+ * completes a transaction, the next iteration can only start new transactions.
+ * If the first iteration does not complete any transaction, the loop will
+ * only iterate once.
+ */
+ private synchronized void processMessageTransactions() {
+ if (!Flags.reliableMessageRetrySupportService()) {
+ return;
+ }
+
+ if (mReliableMessageTransactionFuture != null) {
+ mReliableMessageTransactionFuture.cancel(/* mayInterruptIfRunning= */ false);
+ mReliableMessageTransactionFuture = null;
+ }
+
+ long now = SystemClock.elapsedRealtimeNanos();
+ long nextExecutionTime = Long.MAX_VALUE;
+ boolean continueProcessing;
+ do {
+ continueProcessing = false;
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter =
+ mReliableMessageTransactionMap.entrySet().iterator();
+ while (iter.hasNext()) {
+ ContextHubServiceTransaction transaction = iter.next().getValue();
+ short hostEndpointId = transaction.getHostEndpointId();
+ int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+ if (numCompletedStartCalls == 0
+ && mReliableMessageHostEndpointIdActiveSet.contains(hostEndpointId)) {
+ continue;
+ }
+
+ long nextRetryTime = transaction.getNextRetryTime();
+ long timeoutTime = transaction.getTimeoutTime();
+ boolean transactionTimedOut = timeoutTime <= now;
+ boolean transactionHitMaxRetries = nextRetryTime <= now
+ && numCompletedStartCalls > RELIABLE_MESSAGE_MAX_NUM_RETRY;
+ if (transactionTimedOut || transactionHitMaxRetries) {
+ completeMessageTransaction(transaction,
+ ContextHubTransaction.RESULT_FAILED_TIMEOUT, iter);
+ continueProcessing = true;
+ } else {
+ if (nextRetryTime <= now || numCompletedStartCalls <= 0) {
+ startMessageTransaction(transaction, now);
+ }
+
+ nextExecutionTime = Math.min(nextExecutionTime,
+ transaction.getNextRetryTime());
+ nextExecutionTime = Math.min(nextExecutionTime,
+ transaction.getTimeoutTime());
+ }
+ }
+ } while (continueProcessing);
+
+ if (nextExecutionTime < Long.MAX_VALUE) {
+ mReliableMessageTransactionFuture = mExecutor.schedule(
+ () -> processMessageTransactions(),
+ Math.max(nextExecutionTime - SystemClock.elapsedRealtimeNanos(),
+ RELIABLE_MESSAGE_MIN_WAIT_TIME.toNanos()),
+ TimeUnit.NANOSECONDS);
+ }
+ }
+
+ /**
+ * Completes a message transaction and removes it from the reliable message map.
+ *
+ * @param transaction The transaction to complete.
+ * @param result The result code.
+ */
+ private void completeMessageTransaction(ContextHubServiceTransaction transaction,
+ @ContextHubTransaction.Result int result) {
+ completeMessageTransaction(transaction, result, /* iter= */ null);
+ }
+
+ /**
+ * Completes a message transaction and removes it from the reliable message map using iter.
+ *
+ * @param transaction The transaction to complete.
+ * @param result The result code.
+ * @param iter The iterator for the reliable message map - used to remove the message directly.
+ */
+ private void completeMessageTransaction(ContextHubServiceTransaction transaction,
+ @ContextHubTransaction.Result int result,
+ Iterator<Map.Entry<Integer, ContextHubServiceTransaction>> iter) {
+ transaction.onTransactionComplete(result);
+
+ if (iter == null) {
+ mReliableMessageTransactionMap.remove(transaction.getMessageSequenceNumber());
+ } else {
+ iter.remove();
+ }
+ mReliableMessageHostEndpointIdActiveSet.remove(transaction.getHostEndpointId());
+
+ Log.d(TAG, "Successfully completed reliable message transaction with "
+ + "message sequence number: " + transaction.getMessageSequenceNumber()
+ + " and result: " + result);
+ }
+
+ /**
+ * Starts a message transaction.
+ *
+ * @param transaction The transaction to start.
+ * @param now The now time.
+ */
+ private void startMessageTransaction(ContextHubServiceTransaction transaction, long now) {
+ int numCompletedStartCalls = transaction.getNumCompletedStartCalls();
+ @ContextHubTransaction.Result int result = transaction.onTransact();
+ if (result == ContextHubTransaction.RESULT_SUCCESS) {
+ Log.d(TAG, "Successfully "
+ + (numCompletedStartCalls == 0 ? "started" : "retried")
+ + " reliable message transaction with message sequence number: "
+ + transaction.getMessageSequenceNumber());
+ } else {
+ Log.w(TAG, "Could not start reliable message transaction with "
+ + "message sequence number: "
+ + transaction.getMessageSequenceNumber()
+ + ", result: " + result);
+ }
+
+ transaction.setNextRetryTime(now + RELIABLE_MESSAGE_RETRY_WAIT_TIME.toNanos());
+ if (transaction.getTimeoutTime() == Long.MAX_VALUE) { // first time starting transaction
+ transaction.setTimeoutTime(now + RELIABLE_MESSAGE_TIMEOUT.toNanos());
+ }
+ transaction.setNumCompletedStartCalls(numCompletedStartCalls + 1);
+ mReliableMessageHostEndpointIdActiveSet.add(transaction.getHostEndpointId());
+ }
+
private int toStatsTransactionResult(@ContextHubTransaction.Result int result) {
switch (result) {
case ContextHubTransaction.RESULT_SUCCESS:
@@ -605,19 +772,34 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder(100);
- ContextHubServiceTransaction[] arr;
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
synchronized (this) {
- arr = mTransactionQueue.toArray(new ContextHubServiceTransaction[0]);
- }
- for (int i = 0; i < arr.length; i++) {
- sb.append(i + ": " + arr[i] + "\n");
- }
+ for (ContextHubServiceTransaction transaction: mTransactionQueue) {
+ sb.append(i);
+ sb.append(": ");
+ sb.append(transaction.toString());
+ sb.append("\n");
+ ++i;
+ }
- sb.append("Transaction History:\n");
- Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
- while (iterator.hasNext()) {
- sb.append(iterator.next() + "\n");
+ if (Flags.reliableMessageRetrySupportService()) {
+ for (ContextHubServiceTransaction transaction:
+ mReliableMessageTransactionMap.values()) {
+ sb.append(i);
+ sb.append(": ");
+ sb.append(transaction.toString());
+ sb.append("\n");
+ ++i;
+ }
+ }
+
+ sb.append("Transaction History:\n");
+ Iterator<TransactionRecord> iterator = mTransactionRecordDeque.descendingIterator();
+ while (iterator.hasNext()) {
+ sb.append(iterator.next());
+ sb.append("\n");
+ }
}
return sb.toString();
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 552809b..4fc3d87 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -52,6 +52,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* @hide
@@ -432,10 +433,16 @@
// Use this thread in case where the execution requires to be on a service thread.
// For instance, AppOpsManager.noteOp requires the UPDATE_APP_OPS_STATS permission.
- private HandlerThread mHandlerThread =
+ private final HandlerThread mHandlerThread =
new HandlerThread("Context Hub AIDL callback", Process.THREAD_PRIORITY_BACKGROUND);
private Handler mHandler;
+ // True if test mode is enabled for the Context Hub
+ private final AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false);
+
+ // The test mode manager that manages behaviors during test mode
+ private final ContextHubTestModeManager mTestModeManager = new ContextHubTestModeManager();
+
private class ContextHubAidlCallback extends
android.hardware.contexthub.IContextHubCallback.Stub {
private final int mContextHubId;
@@ -549,6 +556,8 @@
} else {
Log.e(TAG, "mHandleServiceRestartCallback is not set");
}
+
+ mIsTestModeEnabled.set(false);
}
public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException {
@@ -659,7 +668,17 @@
try {
var msg = ContextHubServiceUtil.createAidlContextHubMessage(
hostEndpointId, message);
- hub.sendMessageToHub(contextHubId, msg);
+
+ // Only process the message normally if not using test mode manager or if
+ // the test mode manager call returned false as this indicates it did not
+ // process the message.
+ boolean useTestModeManager = Flags.reliableMessageImplementation()
+ && Flags.reliableMessageTestModeBehavior()
+ && mIsTestModeEnabled.get();
+ if (!useTestModeManager || !mTestModeManager.sendMessageToContextHub(message)) {
+ hub.sendMessageToHub(contextHubId, msg);
+ }
+
return ContextHubTransaction.RESULT_SUCCESS;
} catch (RemoteException | ServiceSpecificException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
@@ -828,6 +847,7 @@
return false;
}
+ mIsTestModeEnabled.set(enable);
try {
hub.setTestMode(enable);
return true;
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 286e789..1938150 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -68,7 +68,6 @@
import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.LocationResult.BadLocationException;
-import android.location.flags.Flags;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -1050,22 +1049,10 @@
stopBatching();
if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
- if (Flags.gnssCallStopBeforeSetPositionMode()) {
- GnssPositionMode positionMode = new GnssPositionMode(mPositionMode,
- GNSS_POSITION_RECURRENCE_PERIODIC, mFixInterval,
- /* preferredAccuracy= */ 0,
- /* preferredTime= */ 0,
- mProviderRequest.isLowPower());
- if (!positionMode.equals(mLastPositionMode)) {
- stopNavigating();
- startNavigating();
- }
- } else {
- // change period and/or lowPowerMode
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- mFixInterval, mProviderRequest.isLowPower())) {
- Log.e(TAG, "set_position_mode failed in updateRequirements");
- }
+ // change period and/or lowPowerMode
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ mFixInterval, mProviderRequest.isLowPower())) {
+ Log.e(TAG, "set_position_mode failed in updateRequirements");
}
} else if (!mStarted) {
// start GPS
@@ -1248,32 +1235,11 @@
}
int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
-
- if (Flags.gnssCallStopBeforeSetPositionMode()) {
- boolean success = mGnssNative.setPositionMode(mPositionMode,
- GNSS_POSITION_RECURRENCE_PERIODIC, interval,
- /* preferredAccuracy= */ 0,
- /* preferredTime= */ 0,
- mProviderRequest.isLowPower());
- if (success) {
- mLastPositionMode = new GnssPositionMode(mPositionMode,
- GNSS_POSITION_RECURRENCE_PERIODIC, interval,
- /* preferredAccuracy= */ 0,
- /* preferredTime= */ 0,
- mProviderRequest.isLowPower());
- } else {
- mLastPositionMode = null;
- setStarted(false);
- Log.e(TAG, "set_position_mode failed in startNavigating()");
- return;
- }
- } else {
- if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
- interval, mProviderRequest.isLowPower())) {
- setStarted(false);
- Log.e(TAG, "set_position_mode failed in startNavigating()");
- return;
- }
+ if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
+ interval, mProviderRequest.isLowPower())) {
+ setStarted(false);
+ Log.e(TAG, "set_position_mode failed in startNavigating()");
+ return;
}
if (!mGnssNative.start()) {
setStarted(false);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 22b33dd..ae3a2afb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -254,9 +254,6 @@
private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
- private static final boolean FIX_UNLOCKED_DEVICE_REQUIRED_KEYS =
- android.security.Flags.fixUnlockedDeviceRequiredKeysV2();
-
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
// ConfirmLockPassword/ConfirmLockPattern multiple times. This needs to be at least the duration
@@ -670,7 +667,6 @@
mActivityManager = injector.getActivityManager();
IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_STARTING);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
injector.getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
@@ -909,13 +905,7 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
- if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
- // Notify keystore that a new user was added.
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- AndroidKeyStoreMaintenance.onUserAdded(userHandle);
- }
- } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
+ if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mStorage.prefetchUser(userHandle);
} else if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
@@ -1130,32 +1120,14 @@
// Note: if this migration gets interrupted (e.g. by the device powering off), there
// shouldn't be a problem since this will run again on the next boot, and
// setCeStorageProtection() and initKeystoreSuperKeys(..., true) are idempotent.
- if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
- if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
- for (UserInfo user : mUserManager.getAliveUsers()) {
- removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
- synchronized (mSpManager) {
- migrateUserToSpWithBoundKeysLocked(user.id);
- }
+ if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+ synchronized (mSpManager) {
+ migrateUserToSpWithBoundKeysLocked(user.id);
}
- setBoolean(MIGRATED_SP_FULL, true, 0);
}
- } else {
- if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
- for (UserInfo user : mUserManager.getAliveUsers()) {
- removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
- synchronized (mSpManager) {
- migrateUserToSpWithBoundCeKeyLocked(user.id);
- }
- }
- setString(MIGRATED_SP_CE_ONLY, "true", 0);
- }
-
- if (getBoolean(MIGRATED_SP_FULL, false, 0)) {
- // The FIX_UNLOCKED_DEVICE_REQUIRED_KEYS flag was enabled but then got disabled.
- // Ensure the full migration runs again the next time the flag is enabled...
- setBoolean(MIGRATED_SP_FULL, false, 0);
- }
+ setBoolean(MIGRATED_SP_FULL, true, 0);
}
mThirdPartyAppsStarted = true;
@@ -1163,30 +1135,6 @@
}
@GuardedBy("mSpManager")
- private void migrateUserToSpWithBoundCeKeyLocked(@UserIdInt int userId) {
- if (isUserSecure(userId)) {
- Slogf.d(TAG, "User %d is secured; no migration needed", userId);
- return;
- }
- long protectorId = getCurrentLskfBasedProtectorId(userId);
- if (protectorId == SyntheticPasswordManager.NULL_PROTECTOR_ID) {
- Slogf.i(TAG, "Migrating unsecured user %d to SP-based credential", userId);
- initializeSyntheticPassword(userId);
- } else {
- Slogf.i(TAG, "Existing unsecured user %d has a synthetic password; re-encrypting CE " +
- "key with it", userId);
- AuthenticationResult result = mSpManager.unlockLskfBasedProtector(
- getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
- null);
- if (result.syntheticPassword == null) {
- Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
- return;
- }
- setCeStorageProtection(userId, result.syntheticPassword);
- }
- }
-
- @GuardedBy("mSpManager")
private void migrateUserToSpWithBoundKeysLocked(@UserIdInt int userId) {
if (isUserSecure(userId)) {
Slogf.d(TAG, "User %d is secured; no migration needed", userId);
@@ -1496,11 +1444,6 @@
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
- void setKeystorePassword(byte[] password, int userHandle) {
- AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
- }
-
- @VisibleForTesting /** Note: this method is overridden in unit tests */
void initKeystoreSuperKeys(@UserIdInt int userId, SyntheticPassword sp, boolean allowExisting) {
final byte[] password = sp.deriveKeyStorePassword();
try {
@@ -2237,9 +2180,7 @@
return;
}
onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
- if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
- unlockKeystore(userId, result.syntheticPassword);
- }
+ unlockKeystore(userId, result.syntheticPassword);
unlockCeStorage(userId, result.syntheticPassword);
}
}
@@ -2545,9 +2486,7 @@
// long time, so for now we keep doing it just in case it's ever important. Don't wait
// until initKeystoreSuperKeys() to do this; that can be delayed if the user is being
// created during early boot, and maybe something will use Keystore before then.
- if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
- AndroidKeyStoreMaintenance.onUserAdded(userId);
- }
+ AndroidKeyStoreMaintenance.onUserAdded(userId);
synchronized (mUserCreationAndRemovalLock) {
// During early boot, don't actually create the synthetic password yet, but rather
@@ -2973,9 +2912,7 @@
LockscreenCredential.createNone(), sp, userId);
setCurrentLskfBasedProtectorId(protectorId, userId);
setCeStorageProtection(userId, sp);
- if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
- initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
- }
+ initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
onSyntheticPasswordCreated(userId, sp);
Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
return sp;
@@ -3090,9 +3027,6 @@
if (!mSpManager.hasSidForUser(userId)) {
mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
- if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
- setKeystorePassword(sp.deriveKeyStorePassword(), userId);
- }
}
} else {
// Cache all profile password if they use unified challenge. This will later be used to
@@ -3103,11 +3037,7 @@
gateKeeperClearSecureUserId(userId);
unlockCeStorage(userId, sp);
unlockKeystore(userId, sp);
- if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
- AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
- } else {
- setKeystorePassword(null, userId);
- }
+ AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
removeBiometricsForUser(userId);
}
setCurrentLskfBasedProtectorId(newProtectorId, userId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 3673eb0..56b93e8 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -839,6 +839,13 @@
+ "Disallowed route: "
+ route);
}
+
+ if (route.isSystemRouteType()) {
+ throw new SecurityException(
+ "Only the system is allowed to publish routes with system route types. "
+ + "Disallowed route: "
+ + route);
+ }
}
Connection connection = mConnectionRef.get();
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index efca598..c3ace15 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -19,13 +19,11 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.service.notification.Condition;
-import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
import android.util.Log;
@@ -41,9 +39,6 @@
private static final String TAG = "ConditionProviders.CCP";
private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
- public static final ComponentName COMPONENT =
- new ComponentName("android", CountdownConditionProvider.class.getName());
-
private static final String ACTION = CountdownConditionProvider.class.getName();
private static final int REQUEST_CODE = 100;
private static final String EXTRA_CONDITION_ID = "condition_id";
@@ -60,31 +55,16 @@
}
@Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-
- @Override
public boolean isValidConditionId(Uri id) {
return ZenModeConfig.isValidCountdownConditionId(id);
}
@Override
- public void attachBase(Context base) {
- attachBaseContext(base);
- }
-
- @Override
public void onBootComplete() {
// noop
}
@Override
- public IConditionProvider asInterface() {
- return (IConditionProvider) onBind(null);
- }
-
- @Override
public void dump(PrintWriter pw, DumpFilter filter) {
pw.println(" CountdownConditionProvider:");
pw.print(" mConnected="); pw.println(mConnected);
diff --git a/services/core/java/com/android/server/notification/CustomManualConditionProvider.java b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java
new file mode 100644
index 0000000..9531c5e
--- /dev/null
+++ b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.net.Uri;
+import android.service.notification.ZenModeConfig;
+
+import java.io.PrintWriter;
+
+/**
+ * Condition provider used for custom manual rules (i.e. user-created rules without an automatic
+ * trigger).
+ */
+public class CustomManualConditionProvider extends SystemConditionProviderService {
+
+ private static final String SIMPLE_NAME = CustomManualConditionProvider.class.getSimpleName();
+
+ @Override
+ public boolean isValidConditionId(Uri id) {
+ return ZenModeConfig.isValidCustomManualConditionId(id);
+ }
+
+ @Override
+ public void onBootComplete() {
+ // Nothing to do.
+ }
+
+ @Override
+ public void onConnected() {
+ // No need to keep subscriptions because we won't ever call notifyConditions
+ }
+
+ @Override
+ public void onSubscribe(Uri conditionId) {
+ // No need to keep subscriptions because we won't ever call notifyConditions
+ }
+
+ @Override
+ public void onUnsubscribe(Uri conditionId) {
+ // No need to keep subscriptions because we won't ever call notifyConditions
+ }
+
+ @Override
+ public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) {
+ pw.print(" "); pw.print(SIMPLE_NAME); pw.println(": ENABLED");
+ }
+}
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 4fe7a27..00dd547 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -19,7 +19,6 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -31,7 +30,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.Condition;
-import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.EventInfo;
import android.util.ArraySet;
@@ -54,8 +52,6 @@
private static final String TAG = "ConditionProviders.ECP";
private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
- public static final ComponentName COMPONENT =
- new ComponentName("android", EventConditionProvider.class.getName());
private static final String NOT_SHOWN = "...";
private static final String SIMPLE_NAME = EventConditionProvider.class.getSimpleName();
private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE";
@@ -82,11 +78,6 @@
}
@Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-
- @Override
public boolean isValidConditionId(Uri id) {
return ZenModeConfig.isValidEventConditionId(id);
}
@@ -166,16 +157,6 @@
}
}
- @Override
- public void attachBase(Context base) {
- attachBaseContext(base);
- }
-
- @Override
- public IConditionProvider asInterface() {
- return (IConditionProvider) onBind(null);
- }
-
private void reloadTrackers() {
if (DEBUG) Slog.d(TAG, "reloadTrackers");
for (int i = 0; i < mTrackers.size(); i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b436c8b..a4f534e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -46,6 +46,10 @@
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
+import static android.app.NotificationChannel.NEWS_ID;
+import static android.app.NotificationChannel.PROMOTIONS_ID;
+import static android.app.NotificationChannel.RECS_ID;
+import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
import static android.app.NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED;
@@ -98,6 +102,11 @@
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.Adjustment.KEY_TYPE;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.redactSensitiveNotificationsFromUntrustedListeners;
import static android.service.notification.Flags.redactSensitiveNotificationsBigTextStyle;
@@ -458,7 +467,8 @@
Adjustment.KEY_IMPORTANCE_PROPOSAL,
Adjustment.KEY_SENSITIVE_CONTENT,
Adjustment.KEY_RANKING_SCORE,
- Adjustment.KEY_NOT_CONVERSATION
+ Adjustment.KEY_NOT_CONVERSATION,
+ Adjustment.KEY_TYPE
};
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
@@ -1023,7 +1033,7 @@
summary.getSbn().getNotification().color = summaryAttr.iconColor;
summary.getSbn().getNotification().visibility = summaryAttr.visibility;
mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground,
- mPostNotificationTrackerFactory.newTracker(null)));
+ /* isAppProvided= */ false, mPostNotificationTrackerFactory.newTracker(null)));
}
}
@@ -1650,7 +1660,7 @@
// Force isAppForeground true here, because for sysui's purposes we
// want to adjust the flag behaviour.
mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(),
- r, true /* isAppForeground*/,
+ r, /* isAppForeground= */ true , /* isAppProvided= */ false,
mPostNotificationTrackerFactory.newTracker(null)));
}
}
@@ -1681,7 +1691,7 @@
// want to be able to adjust the flag behaviour.
mHandler.post(
new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r,
- /* foreground= */ true,
+ /* foreground= */ true, /* isAppProvided= */ false,
mPostNotificationTrackerFactory.newTracker(null)));
}
}
@@ -2706,7 +2716,7 @@
enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
r.getSbn().getId(), r.getSbn().getNotification(), userId, muteOnReturn,
- false /* byForegroundService */);
+ /* byForegroundService= */ false, /* isAppProvided= */ false);
} catch (Exception e) {
Slog.e(TAG, "Cannot un-snooze notification", e);
}
@@ -2851,6 +2861,7 @@
final boolean isAppForeground =
mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground,
+ /* isAppProvided= */ false,
mPostNotificationTrackerFactory.newTracker(null)));
}
}
@@ -3763,7 +3774,7 @@
Notification notification, int userId) throws RemoteException {
enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Binder.getCallingPid(), tag, id, notification, userId,
- false /* byForegroundService */);
+ /* byForegroundService= */ false, /* isAppProvided= */ true);
}
@Override
@@ -6608,6 +6619,30 @@
for (String removeKey : toRemove) {
adjustments.remove(removeKey);
}
+ if (android.service.notification.Flags.notificationClassification()
+ && adjustments.containsKey(KEY_TYPE)) {
+ NotificationChannel newChannel = null;
+ int type = adjustments.getInt(KEY_TYPE);
+ if (TYPE_NEWS == type) {
+ newChannel = mPreferencesHelper.getNotificationChannel(
+ r.getSbn().getPackageName(), r.getUid(), NEWS_ID, false);
+ } else if (TYPE_PROMOTION == type) {
+ newChannel = mPreferencesHelper.getNotificationChannel(
+ r.getSbn().getPackageName(), r.getUid(), PROMOTIONS_ID, false);
+ } else if (TYPE_SOCIAL_MEDIA == type) {
+ newChannel = mPreferencesHelper.getNotificationChannel(
+ r.getSbn().getPackageName(), r.getUid(), SOCIAL_MEDIA_ID, false);
+ } else if (TYPE_CONTENT_RECOMMENDATION == type) {
+ newChannel = mPreferencesHelper.getNotificationChannel(
+ r.getSbn().getPackageName(), r.getUid(), RECS_ID, false);
+ }
+ if (newChannel == null) {
+ adjustments.remove(KEY_TYPE);
+ } else {
+ // swap app provided type with the real thing
+ adjustments.putParcelable(KEY_TYPE, newChannel);
+ }
+ }
r.addAdjustment(adjustment);
if (adjustment.getSignals().containsKey(Adjustment.KEY_SENSITIVE_CONTENT)) {
logSensitiveAdjustmentReceived(isPosted,
@@ -7025,7 +7060,7 @@
public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
- userId, false /* byForegroundService */);
+ userId, /* byForegroundService= */ false , /* isAppProvided= */ true);
}
@Override
@@ -7033,7 +7068,7 @@
String tag, int id, Notification notification, int userId,
boolean byForegroundService) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
- userId, byForegroundService);
+ userId, byForegroundService, /* isAppProvided= */ true);
}
@Override
@@ -7245,7 +7280,8 @@
r.getSbn().getInitialPid(), r.getSbn().getTag(),
r.getSbn().getId(), r.getNotification(),
r.getSbn().getUserId(), /* postSilently= */ true,
- /* byForegroundService= */ false);
+ /* byForegroundService= */ false,
+ /* isAppProvided= */ false);
}
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
@@ -7306,19 +7342,21 @@
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
- int incomingUserId, boolean byForegroundService) {
+ int incomingUserId, boolean byForegroundService, boolean isAppProvided) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
- incomingUserId, false /* postSilently */, byForegroundService);
+ incomingUserId, false /* postSilently */, byForegroundService, isAppProvided);
}
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
- int incomingUserId, boolean postSilently, boolean byForegroundService) {
+ int incomingUserId, boolean postSilently, boolean byForegroundService,
+ boolean isAppProvided) {
PostNotificationTracker tracker = acquireWakeLockForPost(pkg, callingUid);
boolean enqueued = false;
try {
enqueued = enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id,
- notification, incomingUserId, postSilently, tracker, byForegroundService);
+ notification, incomingUserId, postSilently, tracker, byForegroundService,
+ isAppProvided);
} finally {
if (!enqueued) {
tracker.cancel();
@@ -7344,7 +7382,7 @@
private boolean enqueueNotificationInternal(final String pkg, final String opPkg, //HUI
final int callingUid, final int callingPid, final String tag, final int id,
final Notification notification, int incomingUserId, boolean postSilently,
- PostNotificationTracker tracker, boolean byForegroundService) {
+ PostNotificationTracker tracker, boolean byForegroundService, boolean isAppProvided) {
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
@@ -7545,7 +7583,8 @@
// Need escalated privileges to get package importance.
final int packageImportance = getPackageImportanceWithIdentity(pkg);
boolean isAppForeground = packageImportance == IMPORTANCE_FOREGROUND;
- mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground, tracker));
+ mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground,
+ /* isAppProvided= */ isAppProvided, tracker));
return true;
}
@@ -7925,6 +7964,7 @@
mHandler.post(
new EnqueueNotificationRunnable(
r.getUser().getIdentifier(), r, isAppForeground,
+ /* isAppProvided= */ false,
mPostNotificationTrackerFactory.newTracker(null)));
}
}
@@ -8495,13 +8535,15 @@
private final NotificationRecord r;
private final int userId;
private final boolean isAppForeground;
+ private final boolean isAppProvided;
private final PostNotificationTracker mTracker;
EnqueueNotificationRunnable(int userId, NotificationRecord r, boolean foreground,
- PostNotificationTracker tracker) {
+ boolean isAppProvided, PostNotificationTracker tracker) {
this.userId = userId;
this.r = r;
this.isAppForeground = foreground;
+ this.isAppProvided = isAppProvided;
this.mTracker = checkNotNull(tracker);
}
@@ -8523,6 +8565,13 @@
*/
private boolean enqueueNotification() {
synchronized (mNotificationLock) {
+ if (android.app.Flags.secureAllowlistToken()) {
+ // allowlistToken is populated by unparceling, so it will be absent if the
+ // EnqueueNotificationRunnable is created directly by NMS (as we do for group
+ // summaries) instead of via notify(). Fix that.
+ r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
+ }
+
final long snoozeAt =
mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
r.getUser().getIdentifier(),
@@ -8586,9 +8635,10 @@
if (old != null) {
enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
}
+ int appProvided = isAppProvided ? 1 : 0;
EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
pkg, id, tag, userId, notification.toString(),
- enqueueStatus);
+ enqueueStatus, appProvided);
}
// tell the assistant service about the notification
@@ -8748,7 +8798,7 @@
// was not autogrouped onPost, to avoid an unnecessary sort.
// We add the autogroup key to the notification without a
// sort here, and it'll be sorted below with extractSignals.
- addAutogroupKeyLocked(key, /*requestSort=*/false);
+ addAutogroupKeyLocked(key, /* requestSort= */false);
}
}
}
@@ -11373,7 +11423,7 @@
record.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
mHandler.post(new EnqueueNotificationRunnable(record.getUser().getIdentifier(),
- record, isAppForeground,
+ record, isAppForeground, /* isAppProvided= */ false,
mPostNotificationTrackerFactory.newTracker(null)));
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0c6a6c8..0d4bdf6 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -710,7 +710,8 @@
if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
final ArrayList<SnoozeCriterion> snoozeCriterionList =
adjustment.getSignals().getParcelableArrayList(
- Adjustment.KEY_SNOOZE_CRITERIA, android.service.notification.SnoozeCriterion.class);
+ Adjustment.KEY_SNOOZE_CRITERIA,
+ android.service.notification.SnoozeCriterion.class);
setSnoozeCriteria(snoozeCriterionList);
EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_SNOOZE_CRITERIA,
snoozeCriterionList.toString());
@@ -736,7 +737,8 @@
}
if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) {
setSystemGeneratedSmartActions(
- signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, android.app.Notification.Action.class));
+ signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS,
+ android.app.Notification.Action.class));
EventLogTags.writeNotificationAdjusted(getKey(),
Adjustment.KEY_CONTEXTUAL_ACTIONS,
getSystemGeneratedSmartActions().toString());
@@ -778,6 +780,14 @@
Adjustment.KEY_SENSITIVE_CONTENT,
Boolean.toString(mSensitiveContent));
}
+ if (android.service.notification.Flags.notificationClassification()
+ && signals.containsKey(Adjustment.KEY_TYPE)) {
+ updateNotificationChannel(signals.getParcelable(Adjustment.KEY_TYPE,
+ NotificationChannel.class));
+ EventLogTags.writeNotificationAdjusted(getKey(),
+ Adjustment.KEY_TYPE,
+ mChannel.getId());
+ }
if (!signals.isEmpty() && adjustment.getIssuer() != null) {
mAdjustmentIssuer = adjustment.getIssuer();
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 309e945..9ee05d8 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -22,11 +22,14 @@
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.Flags.notificationClassification;
+
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -514,6 +517,10 @@
Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
}
+ if (notificationClassification()) {
+ addReservedChannelsLocked(r);
+ }
+
if (r.uid == UNKNOWN_UID) {
if (Flags.persistIncompleteRestoreData()) {
r.userId = userId;
@@ -603,6 +610,40 @@
return true;
}
+ private void addReservedChannelsLocked(PackagePreferences p) {
+ if (!p.channels.containsKey(NotificationChannel.PROMOTIONS_ID)) {
+ NotificationChannel channel = new NotificationChannel(
+ NotificationChannel.PROMOTIONS_ID,
+ mContext.getString(R.string.promotional_notification_channel_label),
+ IMPORTANCE_LOW);
+ p.channels.put(channel.getId(), channel);
+ }
+
+ if (!p.channels.containsKey(NotificationChannel.RECS_ID)) {
+ NotificationChannel channel = new NotificationChannel(
+ NotificationChannel.RECS_ID,
+ mContext.getString(R.string.recs_notification_channel_label),
+ IMPORTANCE_LOW);
+ p.channels.put(channel.getId(), channel);
+ }
+
+ if (!p.channels.containsKey(NotificationChannel.NEWS_ID)) {
+ NotificationChannel channel = new NotificationChannel(
+ NotificationChannel.NEWS_ID,
+ mContext.getString(R.string.news_notification_channel_label),
+ IMPORTANCE_LOW);
+ p.channels.put(channel.getId(), channel);
+ }
+
+ if (!p.channels.containsKey(NotificationChannel.SOCIAL_MEDIA_ID)) {
+ NotificationChannel channel = new NotificationChannel(
+ NotificationChannel.SOCIAL_MEDIA_ID,
+ mContext.getString(R.string.social_notification_channel_label),
+ IMPORTANCE_LOW);
+ p.channels.put(channel.getId(), channel);
+ }
+ }
+
public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
out.startTag(null, TAG_RANKING);
out.attributeInt(null, ATT_VERSION, XML_VERSION);
@@ -1801,7 +1842,7 @@
public boolean onlyHasDefaultChannel(String pkg, int uid) {
synchronized (mPackagePreferences) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
- if (r.channels.size() == 1
+ if (r.channels.size() == (notificationClassification() ? 5 : 1)
&& r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
return true;
}
@@ -2611,6 +2652,7 @@
context.getResources().getString(
R.string.default_notification_channel_label));
}
+ // TODO (b/346396459): Localize all reserved channels
}
}
}
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 737353d..6efe88f 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -20,7 +20,6 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -28,7 +27,6 @@
import android.os.Binder;
import android.provider.Settings;
import android.service.notification.Condition;
-import android.service.notification.IConditionProvider;
import android.service.notification.ScheduleCalendar;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
@@ -54,8 +52,6 @@
static final String TAG = "ConditionProviders.SCP";
static final boolean DEBUG = true || Log.isLoggable("ConditionProviders", Log.DEBUG);
- public static final ComponentName COMPONENT =
- new ComponentName("android", ScheduleConditionProvider.class.getName());
private static final String NOT_SHOWN = "...";
private static final String SIMPLE_NAME = ScheduleConditionProvider.class.getSimpleName();
private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE";
@@ -66,7 +62,8 @@
private final Context mContext = this;
private final ArrayMap<Uri, ScheduleCalendar> mSubscriptions = new ArrayMap<>();
- private ArraySet<Uri> mSnoozedForAlarm = new ArraySet<>();
+ @GuardedBy("mSnoozedForAlarm")
+ private final ArraySet<Uri> mSnoozedForAlarm = new ArraySet<>();
private AlarmManager mAlarmManager;
private boolean mConnected;
@@ -78,11 +75,6 @@
}
@Override
- public ComponentName getComponent() {
- return COMPONENT;
- }
-
- @Override
public boolean isValidConditionId(Uri id) {
return ZenModeConfig.isValidScheduleConditionId(id);
}
@@ -103,7 +95,10 @@
pw.println(mSubscriptions.get(conditionId).toString());
}
}
- pw.println(" snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozedForAlarm));
+ synchronized (mSnoozedForAlarm) {
+ pw.println(
+ " snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozedForAlarm));
+ }
dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, now);
}
@@ -149,16 +144,6 @@
evaluateSubscriptions();
}
- @Override
- public void attachBase(Context base) {
- attachBaseContext(base);
- }
-
- @Override
- public IConditionProvider asInterface() {
- return (IConditionProvider) onBind(null);
- }
-
private void evaluateSubscriptions() {
if (mAlarmManager == null) {
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
@@ -299,6 +284,7 @@
}
}
+ @GuardedBy("mSnoozedForAlarm")
private void saveSnoozedLocked() {
final String setting = TextUtils.join(SEPARATOR, mSnoozedForAlarm);
final int currentUser = ActivityManager.getCurrentUser();
diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java
index 574f04c..97073b7 100644
--- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java
+++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java
@@ -31,12 +31,21 @@
public abstract class SystemConditionProviderService extends ConditionProviderService {
abstract public void dump(PrintWriter pw, DumpFilter filter);
- abstract public void attachBase(Context context);
- abstract public IConditionProvider asInterface();
- abstract public ComponentName getComponent();
abstract public boolean isValidConditionId(Uri id);
abstract public void onBootComplete();
+ final ComponentName getComponent() {
+ return new ComponentName("android", this.getClass().getName());
+ }
+
+ final IConditionProvider asInterface() {
+ return (IConditionProvider) onBind(null);
+ }
+
+ final void attachBase(Context context) {
+ attachBaseContext(context);
+ }
+
protected static String ts(long time) {
return new Date(time) + " (" + time + ")";
}
diff --git a/services/core/java/com/android/server/notification/TimeToLiveHelper.java b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
index 2facab7..a4460b2 100644
--- a/services/core/java/com/android/server/notification/TimeToLiveHelper.java
+++ b/services/core/java/com/android/server/notification/TimeToLiveHelper.java
@@ -54,13 +54,17 @@
private final AlarmManager mAm;
@VisibleForTesting
+ @GuardedBy("mLock")
final TreeSet<Pair<Long, String>> mKeys;
+ final Object mLock = new Object();
public TimeToLiveHelper(NotificationManagerPrivate nm, Context context) {
mContext = context;
mNm = nm;
mAm = context.getSystemService(AlarmManager.class);
- mKeys = new TreeSet<>((left, right) -> Long.compare(left.first, right.first));
+ synchronized (mLock) {
+ mKeys = new TreeSet<>((left, right) -> Long.compare(left.first, right.first));
+ }
IntentFilter timeoutFilter = new IntentFilter(ACTION);
timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
@@ -73,7 +77,9 @@
}
void dump(PrintWriter pw, String indent) {
- pw.println(indent + "mKeys " + mKeys);
+ synchronized (mLock) {
+ pw.println(indent + "mKeys " + mKeys);
+ }
}
private @NonNull PendingIntent getAlarmPendingIntent(String nextKey, int flags) {
@@ -93,30 +99,35 @@
@VisibleForTesting
void scheduleTimeoutLocked(NotificationRecord record, long currentTime) {
- removeMatchingEntry(record.getKey());
+ synchronized (mLock) {
+ removeMatchingEntry(record.getKey());
- final long timeoutAfter = currentTime + record.getNotification().getTimeoutAfter();
- if (record.getNotification().getTimeoutAfter() > 0) {
- final Long currentEarliestTime = mKeys.isEmpty() ? null : mKeys.first().first;
+ final long timeoutAfter = currentTime + record.getNotification().getTimeoutAfter();
+ if (record.getNotification().getTimeoutAfter() > 0) {
+ final Long currentEarliestTime = mKeys.isEmpty() ? null : mKeys.first().first;
- // Maybe replace alarm with an earlier one
- if (currentEarliestTime == null || timeoutAfter < currentEarliestTime) {
- if (currentEarliestTime != null) {
- cancelFirstAlarm();
+ // Maybe replace alarm with an earlier one
+ if (currentEarliestTime == null || timeoutAfter < currentEarliestTime) {
+ if (currentEarliestTime != null) {
+ cancelFirstAlarm();
+ }
+ mKeys.add(Pair.create(timeoutAfter, record.getKey()));
+ maybeScheduleFirstAlarm();
+ } else {
+ mKeys.add(Pair.create(timeoutAfter, record.getKey()));
}
- mKeys.add(Pair.create(timeoutAfter, record.getKey()));
- maybeScheduleFirstAlarm();
- } else {
- mKeys.add(Pair.create(timeoutAfter, record.getKey()));
}
}
}
@VisibleForTesting
void cancelScheduledTimeoutLocked(NotificationRecord record) {
- removeMatchingEntry(record.getKey());
+ synchronized (mLock) {
+ removeMatchingEntry(record.getKey());
+ }
}
+ @GuardedBy("mLock")
private void removeMatchingEntry(String key) {
if (!mKeys.isEmpty() && key.equals(mKeys.first().second)) {
// cancel the first alarm, remove the first entry, maybe schedule the alarm for the new
@@ -139,11 +150,13 @@
}
}
+ @GuardedBy("mLock")
private void cancelFirstAlarm() {
final PendingIntent pi = getAlarmPendingIntent(mKeys.first().second, FLAG_CANCEL_CURRENT);
mAm.cancel(pi);
}
+ @GuardedBy("mLock")
private void maybeScheduleFirstAlarm() {
if (!mKeys.isEmpty()) {
final PendingIntent piNewFirst = getAlarmPendingIntent(mKeys.first().second,
@@ -162,13 +175,17 @@
return;
}
if (ACTION.equals(action)) {
- Pair<Long, String> earliest = mKeys.first();
- String key = intent.getStringExtra(EXTRA_KEY);
- if (!earliest.second.equals(key)) {
- Slog.wtf(TAG, "Alarm triggered but wasn't the earliest we were tracking");
+ String timeoutKey = null;
+ synchronized (mLock) {
+ Pair<Long, String> earliest = mKeys.first();
+ String key = intent.getStringExtra(EXTRA_KEY);
+ if (!earliest.second.equals(key)) {
+ Slog.wtf(TAG, "Alarm triggered but wasn't the earliest we were tracking");
+ }
+ removeMatchingEntry(key);
+ timeoutKey = earliest.second;
}
- removeMatchingEntry(key);
- mNm.timeoutNotification(earliest.second);
+ mNm.timeoutNotification(timeoutKey);
}
}
};
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 02b5f97..3650536 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -58,6 +58,9 @@
if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) {
mConditionProviders.addSystemProvider(new EventConditionProvider());
}
+ if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.CUSTOM_MANUAL_PATH)) {
+ mConditionProviders.addSystemProvider(new CustomManualConditionProvider());
+ }
mConditionProviders.setCallback(this);
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 267291c..c078409 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -34,6 +34,7 @@
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT_USER;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_RESTORE_BACKUP;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_UNKNOWN;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
@@ -1134,6 +1135,26 @@
modified = true;
}
+ // Allow updating the CPS backing system rules (e.g. for custom manual -> schedule)
+ if (Flags.modesUi()
+ && (origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI || origin == UPDATE_ORIGIN_USER)
+ && Objects.equals(rule.pkg, SystemZenRules.PACKAGE_ANDROID)
+ && !Objects.equals(rule.component, azr.getOwner())) {
+ rule.component = azr.getOwner();
+ modified = true;
+ }
+
+ if (Flags.modesUi()) {
+ if (!azr.isEnabled() && (isNew || rule.enabled)) {
+ // Creating a rule as disabled, or disabling a previously enabled rule.
+ // Record whodunit.
+ rule.disabledOrigin = origin;
+ } else if (azr.isEnabled()) {
+ // Enabling or previously enabled. Clear disabler.
+ rule.disabledOrigin = UPDATE_ORIGIN_UNKNOWN;
+ }
+ }
+
if (!Objects.equals(rule.conditionId, azr.getConditionId())) {
rule.conditionId = azr.getConditionId();
modified = true;
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
new file mode 100644
index 0000000..58b14b1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.media.AudioAttributes.USAGE_ALARM;
+
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.media.AudioFocusInfo;
+import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
+import android.media.audiopolicy.AudioPolicy;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.Vibrator;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+public class BackgroundUserSoundNotifier {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = BackgroundUserSoundNotifier.class.getSimpleName();
+ public static final String BUSN_CHANNEL_ID = "bg_user_sound_channel";
+ public static final String BUSN_CHANNEL_NAME = "BackgroundUserSound";
+ private static final String ACTION_MUTE_SOUND = "com.android.server.ACTION_MUTE_BG_USER";
+ private static final String EXTRA_NOTIFICATION_ID = "com.android.server.EXTRA_CLIENT_UID";
+ private static final String EXTRA_CURRENT_USER_ID = "com.android.server.EXTRA_CURRENT_USER_ID";
+ private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER";
+ /** ID of user with notification displayed, -1 if notification is not showing*/
+ private int mUserWithNotification = -1;
+ private final Context mSystemUserContext;
+ @VisibleForTesting
+ final NotificationManager mNotificationManager;
+ private final UserManager mUserManager;
+
+ /**
+ * Facilitates the display of notifications to current user when there is an alarm or timer
+ * going off on background user and allows to manage the sound through actions.
+ */
+ public BackgroundUserSoundNotifier(Context context) {
+ mSystemUserContext = context;
+ mNotificationManager = mSystemUserContext.getSystemService(NotificationManager.class);
+ mUserManager = mSystemUserContext.getSystemService(UserManager.class);
+ NotificationChannel channel = new NotificationChannel(BUSN_CHANNEL_ID, BUSN_CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH);
+ mNotificationManager.createNotificationChannel(channel);
+ setupFocusControlAudioPolicy();
+ }
+
+ private void setupFocusControlAudioPolicy() {
+ // Used to configure our audio policy to handle focus events.
+ // This gives us the ability to decide which audio focus requests to accept and bypasses
+ // the framework ducking logic.
+ ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class);
+
+ registerReceiver(am);
+ BackgroundUserListener bgUserListener = new BackgroundUserListener(mSystemUserContext);
+ AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext);
+ focusControlPolicyBuilder.setLooper(Looper.getMainLooper());
+
+ focusControlPolicyBuilder.setAudioPolicyFocusListener(bgUserListener);
+
+ AudioPolicy mFocusControlAudioPolicy = focusControlPolicyBuilder.build();
+ int status = mSystemUserContext.getSystemService(AudioManager.class)
+ .registerAudioPolicy(mFocusControlAudioPolicy);
+ if (status != AudioManager.SUCCESS) {
+ Log.w(LOG_TAG , "Could not register the service's focus"
+ + " control audio policy, error: " + status);
+ }
+ }
+
+ final class BackgroundUserListener extends AudioPolicy.AudioPolicyFocusListener {
+
+ Context mSystemContext;
+
+ BackgroundUserListener(Context systemContext) {
+ mSystemContext = systemContext;
+ }
+
+ @SuppressLint("MissingPermission")
+ public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
+ try {
+ BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi,
+ mSystemContext.createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), 0));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
+ BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi);
+ }
+ }
+
+ /**
+ * Registers a BroadcastReceiver for actions related to background user sound notifications.
+ * When ACTION_MUTE_SOUND is received, it mutes a background user's alarm sound.
+ * When ACTION_SWITCH_USER is received, a switch to the background user with alarm is started.
+ */
+ private void registerReceiver(ActivityManager service) {
+ BroadcastReceiver backgroundUserNotificationBroadcastReceiver = new BroadcastReceiver() {
+ @SuppressLint("MissingPermission")
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!(intent.hasExtra(EXTRA_NOTIFICATION_ID)
+ && intent.hasExtra(EXTRA_CURRENT_USER_ID)
+ && intent.hasExtra(Intent.EXTRA_USER_ID))) {
+ return;
+ }
+ final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
+
+ if (DEBUG) {
+ Log.d(LOG_TAG,
+ "User with alarm id " + intent.getIntExtra(Intent.EXTRA_USER_ID,
+ -1) + " current user id " + intent.getIntExtra(
+ EXTRA_CURRENT_USER_ID, -1));
+ }
+ mNotificationManager.cancelAsUser(LOG_TAG, notificationId,
+ UserHandle.of(intent.getIntExtra(EXTRA_CURRENT_USER_ID, -1)));
+ if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
+ final AudioManager audioManager =
+ mSystemUserContext.getSystemService(AudioManager.class);
+ if (audioManager != null) {
+ for (AudioPlaybackConfiguration apc :
+ audioManager.getActivePlaybackConfigurations()) {
+ if (apc.getAudioAttributes().getUsage() == USAGE_ALARM) {
+ if (apc.getPlayerProxy() != null) {
+ apc.getPlayerProxy().stop();
+ }
+ }
+ }
+ }
+ Vibrator vibrator = mSystemUserContext.getSystemService(Vibrator.class);
+ if (vibrator != null && vibrator.isVibrating()) {
+ vibrator.cancel();
+ }
+ } else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
+ service.switchUser(intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
+ }
+ }
+ };
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_MUTE_SOUND);
+ filter.addAction(ACTION_SWITCH_USER);
+ mSystemUserContext.registerReceiver(backgroundUserNotificationBroadcastReceiver, filter,
+ Context.RECEIVER_NOT_EXPORTED);
+ }
+
+ /**
+ * Check if sound is coming from background user and show notification is required.
+ */
+ @VisibleForTesting
+ void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context
+ foregroundContext) throws RemoteException {
+ final int userId = UserHandle.getUserId(afi.getClientUid());
+ final int usage = afi.getAttributes().getUsage();
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (userInfo != null && userId != foregroundContext.getUserId()) {
+ //TODO: b/349138482 - Add handling of cases when usage == USAGE_NOTIFICATION_RINGTONE
+ if (usage == USAGE_ALARM) {
+ Intent muteIntent = createIntent(ACTION_MUTE_SOUND, afi, foregroundContext, userId);
+ PendingIntent mutePI = PendingIntent.getBroadcast(mSystemUserContext, 0,
+ muteIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE);
+ Intent switchIntent = createIntent(ACTION_SWITCH_USER, afi, foregroundContext,
+ userId);
+ PendingIntent switchPI = PendingIntent.getBroadcast(mSystemUserContext, 0,
+ switchIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_IMMUTABLE);
+
+ mUserWithNotification = foregroundContext.getUserId();
+ mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(),
+ createNotification(userInfo.name, mutePI, switchPI, foregroundContext),
+ foregroundContext.getUser());
+ }
+ }
+ }
+
+ /**
+ * If notification is present, dismisses it. To be called when the relevant sound loses focus.
+ */
+ private void dismissNotificationIfNecessary(AudioFocusInfo afi) {
+ if (mUserWithNotification >= 0) {
+ mNotificationManager.cancelAsUser(LOG_TAG, afi.getClientUid(),
+ UserHandle.of(mUserWithNotification));
+ }
+ mUserWithNotification = -1;
+ }
+
+ private Intent createIntent(String intentAction, AudioFocusInfo afi, Context fgUserContext,
+ int userId) {
+ final Intent intent = new Intent(intentAction);
+ intent.putExtra(EXTRA_CURRENT_USER_ID, fgUserContext.getUserId());
+ intent.putExtra(EXTRA_NOTIFICATION_ID, afi.getClientUid());
+ intent.putExtra(Intent.EXTRA_USER_ID, userId);
+ return intent;
+ }
+
+ private Notification createNotification(String userName, PendingIntent muteIntent,
+ PendingIntent switchIntent, Context fgContext) {
+ final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm,
+ userName);
+ final int icon = R.drawable.ic_audio_alarm;
+ final Notification.Action mute = new Notification.Action.Builder(null,
+ fgContext.getString(R.string.bg_user_sound_notification_button_mute),
+ muteIntent).build();
+ final Notification.Action switchUser = new Notification.Action.Builder(null,
+ fgContext.getString(R.string.bg_user_sound_notification_button_switch_user),
+ switchIntent).build();
+ return new Notification.Builder(mSystemUserContext, BUSN_CHANNEL_ID)
+ .setSmallIcon(icon)
+ .setTicker(title)
+ .setWhen(0)
+ .setOngoing(true)
+ .setColor(fgContext.getColor(R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentIntent(muteIntent)
+ .setAutoCancel(true)
+ .setActions(mute, switchUser)
+ .setContentText(fgContext.getString(R.string.bg_user_sound_notification_message))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .build();
+ }
+}
+
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index f59ae16..ee0159d 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2207,11 +2207,17 @@
if (PackageManagerServiceUtils.isSystemOrRoot(callingUid)) {
return true;
}
- if (requireFullPermission) {
- return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ boolean permissionGranted = requireFullPermission ? hasPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ : (hasPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS));
+ if (!permissionGranted) {
+ if (Process.isIsolatedUid(callingUid) && isKnownIsolatedComputeApp(callingUid)) {
+ return checkIsolatedOwnerHasPermission(callingUid, requireFullPermission);
+ }
}
- return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ return permissionGranted;
}
/**
@@ -2227,6 +2233,24 @@
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean hasPermission(String permission, int uid) {
+ return mContext.checkPermission(permission, Process.INVALID_PID, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Since isolated process cannot hold permissions, we check the permissions on the owner app
+ * for known isolated_compute_app cases because they belong to the same package.
+ */
+ private boolean checkIsolatedOwnerHasPermission(int callingUid, boolean requireFullPermission) {
+ int ownerUid = getIsolatedOwner(callingUid);
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid);
+ }
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, ownerUid);
+ }
+
public final boolean isCallerSameApp(String packageName, int uid) {
return isCallerSameApp(packageName, uid, false /* resolveIsolatedUid */);
}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 90d6adc..630fcb6 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -185,7 +185,8 @@
removeAppKeySetData = true;
}
- if (!installRequest.isInstallSystem() && !isSystemPackage && !isApex
+ if ((installRequest.getScanFlags() & SCAN_BOOTING) == 0
+ && !installRequest.isInstallSystem() && !isSystemPackage && !isApex
&& signingDetails != null
&& systemPackage != null && systemPackage.getSigningDetails() != null
&& systemPackage.getSigningDetails().checkCapability(
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 69490a8..5b4f310 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -126,10 +126,12 @@
userId, resolveForStart, /*allowDynamicSplits*/ true);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- var args = new SaferIntentUtils.IntentArgs(intent, resolvedType,
- false /* isReceiver */, resolveForStart, filterCallingUid, callingPid);
- args.platformCompat = mPlatformCompat;
- SaferIntentUtils.filterNonExportedComponents(args, query);
+ if (resolveForStart) {
+ var args = new SaferIntentUtils.IntentArgs(intent, resolvedType,
+ false /* isReceiver */, true, filterCallingUid, callingPid);
+ args.platformCompat = mPlatformCompat;
+ SaferIntentUtils.filterNonExportedComponents(args, query);
+ }
final boolean queryMayBeFiltered =
UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index db94d0e..e0e8a03 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -62,6 +62,8 @@
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatsManager;
import android.app.admin.DevicePolicyEventLogger;
@@ -147,6 +149,8 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.SetScreenLockDialogActivity;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.util.DumpUtils;
@@ -330,6 +334,7 @@
private final Context mContext;
private final PackageManagerService mPm;
+
/**
* Lock for packages. If using with {@link #mUsersLock}, {@link #mPackagesLock} should be
* acquired first.
@@ -1070,6 +1075,12 @@
if (isAutoLockingPrivateSpaceOnRestartsEnabled()) {
autoLockPrivateSpace();
}
+
+ showHsumNotificationIfNeeded();
+
+ if (Flags.addUiForSoundsFromBackgroundUsers()) {
+ new BackgroundUserSoundNotifier(mContext);
+ }
}
private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() {
@@ -4163,6 +4174,48 @@
mUpdatingSystemUserMode = true;
}
+ /**
+ * If the device's actual HSUM status differs from that which is defined by its build
+ * configuration, warn the user. Ignores HSUM emulated status, since that isn't relevant.
+ *
+ * The goal is to inform dogfooders that they need to factory reset the device to align their
+ * device with its build configuration.
+ */
+ private void showHsumNotificationIfNeeded() {
+ if (RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER == isHeadlessSystemUserMode()) {
+ // Actual state does match the configuration. Great!
+ return;
+ }
+ if (Build.isDebuggable()
+ && !TextUtils.isEmpty(SystemProperties.get(SYSTEM_USER_MODE_EMULATION_PROPERTY))) {
+ // Ignore any device that has been playing around with HSUM emulation.
+ return;
+ }
+ Slogf.w(LOG_TAG, "Posting warning that device's HSUM status doesn't match the build's.");
+
+ final String title = mContext
+ .getString(R.string.wrong_hsum_configuration_notification_title);
+ final String message = mContext
+ .getString(R.string.wrong_hsum_configuration_notification_message);
+
+ final Notification notification =
+ new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+ .setSmallIcon(R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0)
+ .setColor(mContext.getColor(R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .build();
+
+ final NotificationManager notificationManager =
+ mContext.getSystemService(NotificationManager.class);
+ notificationManager.notifyAsUser(
+ null, SystemMessage.NOTE_WRONG_HSUM_STATUS, notification, UserHandle.ALL);
+ }
private ResilientAtomicFile getUserListFile() {
File tempBackup = new File(mUserListFile.getParent(), mUserListFile.getName() + ".backup");
@@ -5918,6 +5971,7 @@
return userData;
}
+ /** For testing only! Directly, unnaturally removes userId from list of users. */
@VisibleForTesting
void removeUserInfo(@UserIdInt int userId) {
synchronized (mUsersLock) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 28254d0..46e6546 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -274,7 +274,9 @@
mVirtualDeviceManagerInternal =
LocalServices.getService(VirtualDeviceManagerInternal.class);
}
- return mVirtualDeviceManagerInternal.getPersistentIdForDevice(deviceId);
+ return mVirtualDeviceManagerInternal == null
+ ? VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ : mVirtualDeviceManagerInternal.getPersistentIdForDevice(deviceId);
}
@Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index bf90a6c..0cda30f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2090,8 +2090,13 @@
"Home - Long Press");
switch (mLongPressOnHomeBehavior) {
case LONG_PRESS_HOME_ALL_APPS:
- logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
- launchAllAppsAction();
+ if (mHasFeatureLeanback) {
+ launchAllAppsAction();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
+ } else {
+ launchAllAppsViaA11y();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS);
+ }
break;
case LONG_PRESS_HOME_ASSIST:
logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
@@ -3683,12 +3688,17 @@
}
break;
case KeyEvent.KEYCODE_ALL_APPS:
- if (!down) {
- mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
- Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
+ if (firstDown) {
+ if (mHasFeatureLeanback) {
+ mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
+ Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
+ } else {
+ launchAllAppsViaA11y();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS);
+ }
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 11b9e77..f85b8cc 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -17,6 +17,7 @@
package com.android.server.power;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
@@ -197,9 +198,10 @@
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
Executor backgroundExecutor, PowerManagerFlags powerManagerFlags, Injector injector) {
mContext = context;
+ mInjector = (injector == null) ? new RealInjector() : injector;
mFlags = powerManagerFlags;
mBatteryStats = batteryStats;
- mAppOps = mContext.getSystemService(AppOpsManager.class);
+ mAppOps = mInjector.getAppOpsManager(context);
mSuspendBlocker = suspendBlocker;
mPolicy = policy;
mFaceDownDetector = faceDownDetector;
@@ -230,7 +232,6 @@
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
- mInjector = (injector == null) ? new RealInjector() : injector;
mWakeLockLog = mInjector.getWakeLockLog(context);
// Initialize interactive state for battery stats.
try {
@@ -264,6 +265,7 @@
/**
* Called when a wake lock is acquired.
*/
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onWakeLockAcquired(int flags, String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
IWakeLockCallback callback) {
@@ -273,27 +275,28 @@
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- notifyWakeLockListener(callback, tag, true, ownerUid, flags);
- final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
- if (monitorType >= 0) {
- try {
- final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
- && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
- if (workSource != null) {
- mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
- historyTag, monitorType, unimportantForLogging);
- } else {
- mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
- monitorType, unimportantForLogging);
- // XXX need to deal with disabled operations.
- mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
- }
- } catch (RemoteException ex) {
- // Ignore
- }
- }
-
+ notifyWakeLockListener(callback, tag, true, ownerUid, ownerPid, flags, workSource,
+ packageName, historyTag);
if (!mFlags.improveWakelockLatency()) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
+ && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
+ if (workSource != null) {
+ mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType, unimportantForLogging);
+ } else {
+ mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
+ monitorType, unimportantForLogging);
+ // XXX need to deal with disabled operations.
+ mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName,
+ false, null, null);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockAcquired(flags);
@@ -404,6 +407,7 @@
/**
* Called when a wake lock is released.
*/
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onWakeLockReleased(int flags, String tag, String packageName,
int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
IWakeLockCallback callback, int releaseReason) {
@@ -413,23 +417,24 @@
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- notifyWakeLockListener(callback, tag, false, ownerUid, flags);
- final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
- if (monitorType >= 0) {
- try {
- if (workSource != null) {
- mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
- historyTag, monitorType);
- } else {
- mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
- historyTag, monitorType);
- mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
- }
- } catch (RemoteException ex) {
- // Ignore
- }
- }
+ notifyWakeLockListener(callback, tag, false, ownerUid, ownerPid, flags, workSource,
+ packageName, historyTag);
if (!mFlags.improveWakelockLatency()) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ if (workSource != null) {
+ mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
+ historyTag, monitorType);
+ mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, null);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
}
mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
@@ -1049,24 +1054,75 @@
}
private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled,
- int ownerUid, int flags) {
- if (callback != null) {
- long currentTime = mInjector.currentTimeMillis();
- mHandler.post(() -> {
+ int ownerUid, int ownerPid, int flags, WorkSource workSource, String packageName,
+ String historyTag) {
+ mHandler.post(() -> {
+ if (mFlags.improveWakelockLatency()) {
+ long currentTime = mInjector.currentTimeMillis();
+ if (isEnabled) {
+ notifyWakelockAcquisition(tag, ownerUid, ownerPid, flags,
+ workSource, packageName, historyTag, currentTime);
+ } else {
+ notifyWakelockRelease(tag, ownerUid, ownerPid, flags,
+ workSource, packageName, historyTag, currentTime);
+ }
+ }
+
+ if (callback != null) {
try {
- if (mFlags.improveWakelockLatency()) {
- if (isEnabled) {
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
- } else {
- mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
- }
- }
callback.onStateChanged(isEnabled);
} catch (RemoteException e) {
Slog.e(TAG, "Wakelock.mCallback [" + tag + "] is already dead.", e);
}
- });
+ }
+ });
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void notifyWakelockAcquisition(String tag, int ownerUid, int ownerPid, int flags,
+ WorkSource workSource, String packageName, String historyTag, long currentTime) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
+ && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
+ if (workSource != null) {
+ mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType, unimportantForLogging);
+ } else {
+ mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
+ monitorType, unimportantForLogging);
+ // XXX need to deal with disabled operations.
+ mAppOps.startOpNoThrow(
+ AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName,
+ false, null, null);
+ }
+ } catch (RemoteException ex) {
+ // Do Nothing
+ }
}
+ mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void notifyWakelockRelease(String tag, int ownerUid, int ownerPid, int flags,
+ WorkSource workSource, String packageName, String historyTag, long currentTime) {
+ final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ if (monitorType >= 0) {
+ try {
+ if (workSource != null) {
+ mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
+ historyTag, monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
+ historyTag, monitorType);
+ mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, null);
+ }
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+ }
+ mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
}
private final class NotifierHandler extends Handler {
@@ -1114,6 +1170,11 @@
* Gets the WakeLockLog object
*/
WakeLockLog getWakeLockLog(Context context);
+
+ /**
+ * Gets the AppOpsManager system service
+ */
+ AppOpsManager getAppOpsManager(Context context);
}
static class RealInjector implements Injector {
@@ -1126,5 +1187,10 @@
public WakeLockLog getWakeLockLog(Context context) {
return new WakeLockLog(context);
}
+
+ @Override
+ public AppOpsManager getAppOpsManager(Context context) {
+ return context.getSystemService(AppOpsManager.class);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index 94340ec..c1fad33 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -1,6 +1,7 @@
michaelwr@google.com
santoscordon@google.com
-philipjunker@google.com
+petsjonkin@google.com
+brup@google.com
per-file ThermalManagerService.java=file:/THERMAL_OWNERS
per-file LowPowerStandbyController.java=qingxun@google.com
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 6b7f2fa..4b4e442 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -48,6 +48,7 @@
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.vibrator.persistence.VibrationXmlParser;
+import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -743,6 +744,11 @@
*/
@VisibleForTesting // For testing vibrations without shutting down device
void playShutdownVibration(Context context) {
+ if (mInjector.isShutdownVibrationDisabled(context)) {
+ Log.i(TAG, "Vibration disabled in config");
+ return;
+ }
+
Vibrator vibrator = mInjector.getVibrator(context);
if (!vibrator.hasVibrator()) {
return;
@@ -920,5 +926,14 @@
return context.getResources().getString(
com.android.internal.R.string.config_defaultShutdownVibrationFile);
}
+
+ public boolean isShutdownVibrationDisabled(Context context) {
+ boolean disabledInConfig = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_disableShutdownVibrationInZen);
+ boolean isZenMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF)
+ != Settings.Global.ZEN_MODE_OFF;
+ return disabledInConfig && isZenMode;
+ }
}
}
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index ff1d2e4..c6ef89d 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -16,6 +16,8 @@
package com.android.server.power.feature;
+import android.os.Build;
+import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Slog;
@@ -82,7 +84,7 @@
}
return mEnabled;
}
- mEnabled = mFlagFunction.get();
+ mEnabled = flagOrSystemProperty(mFlagFunction, mName);
if (DEBUG) {
Slog.d(TAG, mName + ": mEnabled. Flag value = " + mEnabled);
}
@@ -90,6 +92,15 @@
return mEnabled;
}
+ private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
+ boolean flagValue = flagFunction.get();
+ if (Build.IS_ENG || Build.IS_USERDEBUG) {
+ return SystemProperties.getBoolean("persist.sys." + flagName + "-override",
+ flagValue);
+ }
+ return flagValue;
+ }
+
@Override
public String toString() {
// remove com.android.server.power.feature.flags. from the beginning of the name.
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index fbdba4f..e27f3b2 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -19,16 +19,20 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
+import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import com.android.internal.os.PowerStats;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.power.stats.AggregatedPowerStatsConfig.PowerComponent;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -53,11 +57,14 @@
private static final int MAX_CLOCK_UPDATES = 100;
private static final String XML_TAG_AGGREGATED_POWER_STATS = "agg-power-stats";
- private final PowerComponentAggregatedPowerStats[] mPowerComponentStats;
+ private final AggregatedPowerStatsConfig mConfig;
+ private final SparseArray<PowerComponentAggregatedPowerStats> mPowerComponentStats;
+ private final PowerComponentAggregatedPowerStats mGenericPowerComponent;
static class ClockUpdate {
public long monotonicTime;
- @CurrentTimeMillisLong public long currentTime;
+ @CurrentTimeMillisLong
+ public long currentTime;
}
private final List<ClockUpdate> mClockUpdates = new ArrayList<>();
@@ -65,13 +72,35 @@
@DurationMillisLong
private long mDurationMs;
- AggregatedPowerStats(AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
- List<AggregatedPowerStatsConfig.PowerComponent> configs =
+ AggregatedPowerStats(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
+ mConfig = aggregatedPowerStatsConfig;
+ List<PowerComponent> configs =
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
- mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()];
+ mPowerComponentStats = new SparseArray<>(configs.size());
for (int i = 0; i < configs.size(); i++) {
- mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(this, configs.get(i));
+ PowerComponent powerComponent = configs.get(i);
+ mPowerComponentStats.put(powerComponent.getPowerComponentId(),
+ new PowerComponentAggregatedPowerStats(this, powerComponent));
}
+ mGenericPowerComponent = createGenericPowerComponent();
+ mPowerComponentStats.put(BatteryConsumer.POWER_COMPONENT_ANY, mGenericPowerComponent);
+ }
+
+ private PowerComponentAggregatedPowerStats createGenericPowerComponent() {
+ PowerComponent config = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
+ config.trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+ PowerComponentAggregatedPowerStats stats =
+ new PowerComponentAggregatedPowerStats(this, config);
+ stats.setPowerStatsDescriptor(
+ new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_ANY, 0, null, 0, 0,
+ new PersistableBundle()));
+ return stats;
}
/**
@@ -79,7 +108,7 @@
* there may be multiple clock updates in one set of aggregated stats.
*
* @param monotonicTime monotonic time in milliseconds, see
- * {@link com.android.internal.os.MonotonicClock}
+ * {@link com.android.internal.os.MonotonicClock}
* @param currentTime current time in milliseconds, see {@link System#currentTimeMillis()}
*/
void addClockUpdate(long monotonicTime, @CurrentTimeMillisLong long currentTime) {
@@ -90,7 +119,7 @@
mClockUpdates.add(clockUpdate);
} else {
Slog.i(TAG, "Too many clock updates. Replacing the previous update with "
- + DateFormat.format("yyyy-MM-dd-HH-mm-ss", currentTime));
+ + DateFormat.format("yyyy-MM-dd-HH-mm-ss", currentTime));
mClockUpdates.set(mClockUpdates.size() - 1, clockUpdate);
}
}
@@ -119,66 +148,97 @@
return mDurationMs;
}
- PowerComponentAggregatedPowerStats getPowerComponentStats(int powerComponentId) {
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- if (stats.powerComponentId == powerComponentId) {
- return stats;
+ List<PowerComponentAggregatedPowerStats> getPowerComponentStats() {
+ List<PowerComponentAggregatedPowerStats> list = new ArrayList<>(
+ mPowerComponentStats.size());
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
+ if (stats != mGenericPowerComponent) {
+ list.add(stats);
}
}
- return null;
+ return list;
+ }
+
+ PowerComponentAggregatedPowerStats getPowerComponentStats(int powerComponentId) {
+ return mPowerComponentStats.get(powerComponentId);
+ }
+
+ void start(long timestampMs) {
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ PowerComponentAggregatedPowerStats component = mPowerComponentStats.valueAt(i);
+ component.getConfig().getProcessor().start(component, timestampMs);
+ }
}
void setDeviceState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
long time) {
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- stats.setState(stateId, state, time);
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ mPowerComponentStats.valueAt(i).setState(stateId, state, time);
}
}
void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
long time) {
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- stats.setUidState(uid, stateId, state, time);
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ mPowerComponentStats.valueAt(i).setUidState(uid, stateId, state, time);
}
}
boolean isCompatible(PowerStats powerStats) {
int powerComponentId = powerStats.descriptor.powerComponentId;
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- if (stats.powerComponentId == powerComponentId && !stats.isCompatible(powerStats)) {
- return false;
- }
- }
- return true;
+ PowerComponentAggregatedPowerStats stats = mPowerComponentStats.get(powerComponentId);
+ return stats != null && stats.isCompatible(powerStats);
}
void addPowerStats(PowerStats powerStats, long time) {
int powerComponentId = powerStats.descriptor.powerComponentId;
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- if (stats.powerComponentId == powerComponentId) {
- stats.getConfig().getProcessor().addPowerStats(stats, powerStats, time);
+ PowerComponentAggregatedPowerStats stats = mPowerComponentStats.get(powerComponentId);
+ if (stats == null) {
+ PowerComponent powerComponent = mConfig.createPowerComponent(powerComponentId);
+ if (powerComponent == null) {
+ return;
}
+
+ stats = new PowerComponentAggregatedPowerStats(this, powerComponent);
+ stats.setPowerStatsDescriptor(powerStats.descriptor);
+ stats.copyStatesFrom(mGenericPowerComponent);
+ mPowerComponentStats.put(powerComponentId, stats);
}
+
+ PowerStatsProcessor processor = stats.getConfig().getProcessor();
+ processor.addPowerStats(stats, powerStats, time);
}
public void noteStateChange(BatteryStats.HistoryItem item) {
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
stats.getConfig().getProcessor().noteStateChange(stats, item);
}
}
+ void finish(long timestampMs) {
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ PowerComponentAggregatedPowerStats component = mPowerComponentStats.valueAt(i);
+ component.getConfig().getProcessor().finish(component, timestampMs);
+ }
+ }
+
void reset() {
mClockUpdates.clear();
mDurationMs = 0;
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- stats.reset();
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ mPowerComponentStats.valueAt(i).reset();
}
}
public void writeXml(TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, XML_TAG_AGGREGATED_POWER_STATS);
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- stats.writeXml(serializer);
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
+ if (stats != mGenericPowerComponent) {
+ stats.writeXml(serializer);
+ }
}
serializer.endTag(null, XML_TAG_AGGREGATED_POWER_STATS);
serializer.flush();
@@ -200,23 +260,34 @@
case XML_TAG_AGGREGATED_POWER_STATS:
inElement = true;
break;
- case PowerComponentAggregatedPowerStats.XML_TAG_POWER_COMPONENT:
+ case PowerComponentAggregatedPowerStats.XML_TAG_POWER_COMPONENT: {
if (!inElement) {
break;
}
int powerComponentId = parser.getAttributeInt(null,
PowerComponentAggregatedPowerStats.XML_ATTR_ID);
- for (PowerComponentAggregatedPowerStats powerComponent :
- stats.mPowerComponentStats) {
- if (powerComponent.powerComponentId == powerComponentId) {
- if (!powerComponent.readFromXml(parser)) {
- skipToEnd = true;
- }
- break;
+
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(powerComponentId);
+ if (powerComponentStats == null) {
+ PowerComponent powerComponent =
+ aggregatedPowerStatsConfig.createPowerComponent(
+ powerComponentId);
+ if (powerComponent != null) {
+ powerComponentStats = new PowerComponentAggregatedPowerStats(stats,
+ powerComponent);
+ stats.mPowerComponentStats.put(powerComponentId,
+ powerComponentStats);
+ }
+ }
+ if (powerComponentStats != null) {
+ if (!powerComponentStats.readFromXml(parser)) {
+ skipToEnd = true;
}
}
break;
+ }
}
}
eventType = parser.next();
@@ -254,14 +325,14 @@
ipw.println("Device");
ipw.increaseIndent();
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- stats.dumpDevice(ipw);
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ mPowerComponentStats.valueAt(i).dumpDevice(ipw);
}
ipw.decreaseIndent();
Set<Integer> uids = new HashSet<>();
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- stats.collectUids(uids);
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ mPowerComponentStats.valueAt(i).collectUids(uids);
}
Integer[] allUids = uids.toArray(new Integer[uids.size()]);
@@ -269,8 +340,8 @@
for (int uid : allUids) {
ipw.println(UserHandle.formatUid(uid));
ipw.increaseIndent();
- for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
- stats.dumpUid(ipw, uid);
+ for (int i = 0; i < mPowerComponentStats.size(); i++) {
+ mPowerComponentStats.valueAt(i).dumpUid(ipw, uid);
}
ipw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
index 1ff7cb7..1f4a391 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -17,12 +17,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.BatteryConsumer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Supplier;
/**
* Configuration that controls how power stats are aggregated. It determines which state changes
@@ -140,7 +142,7 @@
}
@NonNull
- public PowerStatsProcessor getProcessor() {
+ PowerStatsProcessor getProcessor() {
return mProcessor;
}
@@ -160,6 +162,8 @@
}
private final List<PowerComponent> mPowerComponents = new ArrayList<>();
+ private PowerComponent mCustomPowerComponent;
+ private Supplier<PowerStatsProcessor> mCustomPowerStatsProcessorFactory;
/**
* Creates a configuration for the specified power component, which may be one of the
@@ -199,10 +203,45 @@
return powerComponent;
}
+ /**
+ * Creates a configuration for custom power components, which are yet to be discovered
+ * dynamically through the integration with PowerStatsService.
+ */
+ public PowerComponent trackCustomPowerComponents(
+ Supplier<PowerStatsProcessor> processorFactory) {
+ mCustomPowerStatsProcessorFactory = processorFactory;
+ mCustomPowerComponent = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
+ return mCustomPowerComponent;
+ }
+
+ /**
+ * Returns configurations for all registered or dynamically discovered power components.
+ */
public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
return mPowerComponents;
}
+ /**
+ * Creates a configuration for a custom power component discovered dynamically through the
+ * integration with PowerStatsService.
+ */
+ @Nullable
+ public PowerComponent createPowerComponent(int powerComponentId) {
+ if (mCustomPowerComponent == null) {
+ return null;
+ }
+
+ PowerComponent powerComponent = new PowerComponent(powerComponentId);
+ powerComponent.trackDeviceStates(mCustomPowerComponent.mTrackedDeviceStates);
+ powerComponent.trackUidStates(mCustomPowerComponent.mTrackedUidStates);
+
+ if (mCustomPowerStatsProcessorFactory != null) {
+ powerComponent.setProcessor(mCustomPowerStatsProcessorFactory.get());
+ }
+
+ return powerComponent;
+ }
+
private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() {
@Override
void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 322ed86..eabc979 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -300,6 +300,7 @@
private final BluetoothPowerStatsCollector mBluetoothPowerStatsCollector;
private final CameraPowerStatsCollector mCameraPowerStatsCollector;
private final GnssPowerStatsCollector mGnssPowerStatsCollector;
+ private final CustomEnergyConsumerPowerStatsCollector mCustomEnergyConsumerPowerStatsCollector;
private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever =
new WifiPowerStatsCollector.WifiStatsRetriever() {
@@ -11303,6 +11304,10 @@
mGnssPowerStatsCollector = new GnssPowerStatsCollector(mPowerStatsCollectorInjector);
mGnssPowerStatsCollector.addConsumer(this::recordPowerStats);
+ mCustomEnergyConsumerPowerStatsCollector =
+ new CustomEnergyConsumerPowerStatsCollector(mPowerStatsCollectorInjector);
+ mCustomEnergyConsumerPowerStatsCollector.addConsumer(this::recordPowerStats);
+
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -13922,6 +13927,11 @@
* Read and record Rail Energy data.
*/
public void updateRailStatsLocked() {
+ if (mCustomEnergyConsumerPowerStatsCollector.isEnabled()) {
+ mCustomEnergyConsumerPowerStatsCollector.schedule();
+ return;
+ }
+
if (mEnergyConsumerRetriever == null || !mTmpRailStats.isRailStatsAvailable()) {
return;
}
@@ -14733,6 +14743,10 @@
mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS));
mGnssPowerStatsCollector.schedule();
+ mCustomEnergyConsumerPowerStatsCollector.setEnabled(
+ mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_ANY));
+ mCustomEnergyConsumerPowerStatsCollector.schedule();
+
mSystemReady = true;
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index ce0ee39..8127b82 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -113,7 +113,9 @@
mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
- mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_ANY)) {
+ mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
+ }
mPowerCalculators.add(new UserPowerCalculator());
if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
index 64c3446..502337c 100644
--- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
@@ -16,16 +16,9 @@
package com.android.server.power.stats;
-class BinaryStatePowerStatsLayout extends PowerStatsLayout {
+class BinaryStatePowerStatsLayout extends EnergyConsumerPowerStatsLayout {
BinaryStatePowerStatsLayout() {
addDeviceSectionUsageDuration();
- // Add a section for consumed energy, even if the specific device does not
- // have support EnergyConsumers. This is done to guarantee format compatibility between
- // PowerStats created by a PowerStatsCollector and those produced by a PowerStatsProcessor.
- addDeviceSectionEnergyConsumers(1);
- addDeviceSectionPowerEstimate();
-
addUidSectionUsageDuration();
- addUidSectionPowerEstimate();
}
}
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
new file mode 100644
index 0000000..1191901
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CustomEnergyConsumerPowerStatsCollector extends PowerStatsCollector {
+ private static final EnergyConsumerPowerStatsLayout sLayout =
+ new EnergyConsumerPowerStatsLayout();
+ private final EnergyConsumerPowerStatsCollector.Injector mInjector;
+ private List<EnergyConsumerPowerStatsCollector> mCollectors;
+
+ CustomEnergyConsumerPowerStatsCollector(EnergyConsumerPowerStatsCollector.Injector injector) {
+ super(injector.getHandler(), 0, injector.getUidResolver(), injector.getClock());
+ mInjector = injector;
+ }
+
+ protected void ensureInitialized() {
+ if (mCollectors != null) {
+ return;
+ }
+
+ ConsumedEnergyRetriever retriever = mInjector.getConsumedEnergyRetriever();
+ int[] energyConsumerIds = retriever.getEnergyConsumerIds(EnergyConsumerType.OTHER);
+ int powerComponentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+ mCollectors = new ArrayList<>(energyConsumerIds.length);
+ for (int i = 0; i < energyConsumerIds.length; i++) {
+ String name = retriever.getEnergyConsumerName(energyConsumerIds[i]);
+ EnergyConsumerPowerStatsCollector collector = new EnergyConsumerPowerStatsCollector(
+ mInjector, powerComponentId++, name, energyConsumerIds[i], sLayout);
+ collector.setEnabled(true);
+ collector.addConsumer(this::deliverStats);
+ mCollectors.add(collector);
+ }
+ }
+
+ @Override
+ public boolean schedule() {
+ if (!isEnabled()) {
+ return false;
+ }
+
+ ensureInitialized();
+ boolean success = false;
+ for (int i = 0; i < mCollectors.size(); i++) {
+ success |= mCollectors.get(i).schedule();
+ }
+ return success;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java
new file mode 100644
index 0000000..a86242a
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
+ private static final EnergyConsumerPowerStatsLayout sLayout =
+ new EnergyConsumerPowerStatsLayout();
+ private long[] mTmpDeviceStatsArray;
+ private long[] mTmpUidStatsArray;
+ private PowerEstimationPlan mPlan;
+
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+ PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+ mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+ mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+ if (mPlan == null) {
+ mPlan = new PowerEstimationPlan(stats.getConfig());
+ }
+
+ computeDevicePowerEstimates(stats);
+
+ List<Integer> uids = new ArrayList<>();
+ stats.collectUids(uids);
+
+ if (!uids.isEmpty()) {
+ computeUidPowerEstimates(stats, uids);
+ }
+ }
+
+ private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ continue;
+ }
+
+ sLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+ uCtoMah(sLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0)));
+ stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+ }
+ }
+
+ private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+ List<Integer> uids) {
+ for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+ UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+ List<UidStateProportionalEstimate> proportionalEstimates =
+ uidStateEstimate.proportionalEstimates;
+ for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+ UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
+ for (int k = uids.size() - 1; k >= 0; k--) {
+ int uid = uids.get(k);
+ if (stats.getUidStats(mTmpUidStatsArray, uid,
+ proportionalEstimate.stateValues)) {
+ sLayout.setUidPowerEstimate(mTmpUidStatsArray,
+ uCtoMah(sLayout.getUidConsumedEnergy(mTmpUidStatsArray, 0)));
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
index 2021f85..cace941 100644
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
@@ -16,10 +16,13 @@
package com.android.server.power.stats;
+import android.hardware.power.stats.EnergyConsumerAttribution;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.Handler;
import android.os.PersistableBundle;
import android.util.Slog;
+import android.util.SparseLongArray;
import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;
@@ -27,9 +30,7 @@
import java.util.function.IntSupplier;
public class EnergyConsumerPowerStatsCollector extends PowerStatsCollector {
- private static final String TAG = "CameraPowerStatsCollector";
-
- private static final long CAMERA_ACTIVITY_REQUEST_TIMEOUT = 20000;
+ private static final String TAG = "EnergyConsumerPowerStatsCollector";
private static final long ENERGY_UNSPECIFIED = -1;
@@ -48,21 +49,22 @@
private final int mEnergyConsumerType;
private final String mEnergyConsumerName;
- private final BinaryStatePowerStatsLayout mLayout;
+ private final EnergyConsumerPowerStatsLayout mLayout;
private boolean mIsInitialized;
private PowerStats mPowerStats;
private ConsumedEnergyRetriever mConsumedEnergyRetriever;
private IntSupplier mVoltageSupplier;
- private int[] mEnergyConsumerIds = new int[0];
+ private int[] mEnergyConsumerIds;
private long mLastConsumedEnergyUws = ENERGY_UNSPECIFIED;
+ private SparseLongArray mLastConsumerEnergyPerUid = new SparseLongArray();
private int mLastVoltageMv;
private long mLastUpdateTimestamp;
private boolean mFirstCollection = true;
EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
String powerComponentName, @EnergyConsumerType int energyConsumerType,
- String energyConsumerName, BinaryStatePowerStatsLayout statsLayout) {
+ String energyConsumerName, EnergyConsumerPowerStatsLayout statsLayout) {
super(injector.getHandler(),
injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
injector.getUidResolver(), injector.getClock());
@@ -74,6 +76,21 @@
mLayout = statsLayout;
}
+ EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
+ String powerComponentName, int energyConsumerId,
+ EnergyConsumerPowerStatsLayout statsLayout) {
+ super(injector.getHandler(),
+ injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
+ injector.getUidResolver(), injector.getClock());
+ mInjector = injector;
+ mPowerComponentId = powerComponentId;
+ mPowerComponentName = powerComponentName;
+ mEnergyConsumerIds = new int[]{energyConsumerId};
+ mEnergyConsumerType = EnergyConsumerType.OTHER;
+ mEnergyConsumerName = null;
+ mLayout = statsLayout;
+ }
+
private boolean ensureInitialized() {
if (mIsInitialized) {
return true;
@@ -85,9 +102,10 @@
mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
mVoltageSupplier = mInjector.getVoltageSupplier();
- mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType,
- mEnergyConsumerName);
-
+ if (mEnergyConsumerIds == null) {
+ mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType,
+ mEnergyConsumerName);
+ }
PersistableBundle extras = new PersistableBundle();
mLayout.toExtras(extras);
PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
@@ -110,18 +128,23 @@
return null;
}
- long consumedEnergy = 0;
int voltageMv = mVoltageSupplier.getAsInt();
- if (voltageMv <= 0) {
+ int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+ if (averageVoltage <= 0) {
Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+ " mV) when querying energy consumers");
- } else {
- long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
- if (energyUws != null) {
- for (int i = energyUws.length - 1; i >= 0; i--) {
- if (energyUws[i] != ENERGY_UNSPECIFIED) {
- consumedEnergy += energyUws[i];
- }
+ return null;
+ }
+
+ mLastVoltageMv = voltageMv;
+
+ EnergyConsumerResult[] energy =
+ mConsumedEnergyRetriever.getConsumedEnergy(mEnergyConsumerIds);
+ long consumedEnergy = 0;
+ if (energy != null) {
+ for (int i = energy.length - 1; i >= 0; i--) {
+ if (energy[i].energyUWs != ENERGY_UNSPECIFIED) {
+ consumedEnergy += energy[i].energyUWs;
}
}
}
@@ -138,13 +161,47 @@
return null;
}
- int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
- mLastVoltageMv = voltageMv;
mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage));
+
+ for (int i = mPowerStats.uidStats.size() - 1; i >= 0; i--) {
+ mLayout.setUidConsumedEnergy(mPowerStats.uidStats.valueAt(i), 0, 0);
+ }
+
+ if (energy != null) {
+ for (int i = energy.length - 1; i >= 0; i--) {
+ EnergyConsumerAttribution[] perUid = energy[i].attribution;
+ if (perUid == null) {
+ continue;
+ }
+
+ for (EnergyConsumerAttribution attribution : perUid) {
+ int uid = mUidResolver.mapUid(attribution.uid);
+ long lastEnergy = mLastConsumerEnergyPerUid.get(uid);
+ long deltaEnergy = attribution.energyUWs - lastEnergy;
+ mLastConsumerEnergyPerUid.put(uid, attribution.energyUWs);
+ if (deltaEnergy <= 0) {
+ continue;
+ }
+ long[] uidStats = mPowerStats.uidStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new long[mLayout.getUidStatsArrayLength()];
+ mPowerStats.uidStats.put(uid, uidStats);
+ }
+
+ mLayout.setUidConsumedEnergy(uidStats, 0,
+ mLayout.getUidConsumedEnergy(uidStats, 0) + deltaEnergy);
+ }
+ }
+ }
long timestamp = mClock.elapsedRealtime();
mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
mLastUpdateTimestamp = timestamp;
mFirstCollection = false;
return mPowerStats;
}
+
+ @Override
+ protected void onUidRemoved(int uid) {
+ mLastConsumerEnergyPerUid.delete(uid);
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java
new file mode 100644
index 0000000..8430f56
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java
@@ -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.
+ */
+
+package com.android.server.power.stats;
+
+class EnergyConsumerPowerStatsLayout extends PowerStatsLayout {
+ EnergyConsumerPowerStatsLayout() {
+ // Add a section for consumed energy, even if the specific device does not
+ // have support EnergyConsumers. This is done to guarantee format compatibility between
+ // PowerStats created by a PowerStatsCollector and those produced by a PowerStatsProcessor.
+ addDeviceSectionEnergyConsumers(1);
+ addDeviceSectionPowerEstimate();
+
+ // Allocate a cell for per-UID consumed energy attribution. We won't know whether the
+ // corresponding energy consumer does per-UID attribution until we get data from
+ // PowerStatsService.
+ addUidSectionEnergyConsumers(1);
+ addUidSectionPowerEstimate();
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 85a2293..8384b2b 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.os.PowerStats;
@@ -44,6 +45,7 @@
* as part of the {@link PowerStats.Descriptor}.
*/
class PowerComponentAggregatedPowerStats {
+ private static final String TAG = "AggregatePowerStats";
static final String XML_TAG_POWER_COMPONENT = "power_component";
static final String XML_ATTR_ID = "id";
private static final String XML_TAG_DEVICE_STATS = "device-stats";
@@ -372,6 +374,37 @@
}
}
+ void copyStatesFrom(PowerComponentAggregatedPowerStats source) {
+ if (source.mDeviceStates.length == mDeviceStates.length) {
+ System.arraycopy(source.mDeviceStates, 0, mDeviceStates, 0, mDeviceStates.length);
+ if (source.mDeviceStats != null) {
+ createDeviceStats(0);
+ if (mDeviceStats != null) {
+ mDeviceStats.copyStatesFrom(source.mDeviceStats);
+ }
+ }
+ } else {
+ Slog.wtf(TAG, "State configurations have different lengths: "
+ + source.mDeviceStates.length + " vs " + mDeviceStates.length);
+ }
+ for (int i = source.mUidStats.size() - 1; i >= 0; i--) {
+ int uid = source.mUidStats.keyAt(i);
+ UidStats sourceUidStats = source.mUidStats.valueAt(i);
+ if (sourceUidStats.states == null) {
+ continue;
+ }
+ UidStats uidStats = new UidStats();
+ uidStats.states = Arrays.copyOf(sourceUidStats.states, sourceUidStats.states.length);
+ if (sourceUidStats.stats != null) {
+ createUidStats(uidStats, 0);
+ if (uidStats.stats != null) {
+ uidStats.stats.copyStatesFrom(sourceUidStats.stats);
+ }
+ }
+ mUidStats.put(uid, uidStats);
+ }
+ }
+
public void writeXml(TypedXmlSerializer serializer) throws IOException {
// No stats aggregated - can skip writing XML altogether
if (mPowerStatsDescriptor == null) {
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index 6e7fdf1..86f515c 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -15,8 +15,8 @@
*/
package com.android.server.power.stats;
+import android.annotation.NonNull;
import android.os.BatteryStats;
-import android.util.SparseArray;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
@@ -32,20 +32,14 @@
private static final long UNINITIALIZED = -1;
private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private final BatteryStatsHistory mHistory;
- private final SparseArray<PowerStatsProcessor> mProcessors = new SparseArray<>();
private AggregatedPowerStats mStats;
private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
- public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
- BatteryStatsHistory history) {
+ public PowerStatsAggregator(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
+ @NonNull BatteryStatsHistory history) {
mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
mHistory = history;
- for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig :
- aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) {
- PowerStatsProcessor processor = powerComponentsConfig.getProcessor();
- mProcessors.put(powerComponentsConfig.getPowerComponentId(), processor);
- }
}
AggregatedPowerStatsConfig getConfig() {
@@ -71,7 +65,7 @@
mStats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
}
- start(mStats, startTimeMs);
+ mStats.start(startTimeMs);
boolean clockUpdateAdded = false;
long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED;
@@ -138,7 +132,7 @@
if (!mStats.isCompatible(item.powerStats)) {
if (lastTime > baseTime) {
mStats.setDuration(lastTime - baseTime);
- finish(mStats, lastTime);
+ mStats.finish(lastTime);
consumer.accept(mStats);
}
mStats.reset();
@@ -151,7 +145,7 @@
}
if (lastTime > baseTime) {
mStats.setDuration(lastTime - baseTime);
- finish(mStats, lastTime);
+ mStats.finish(lastTime);
consumer.accept(mStats);
}
@@ -159,26 +153,6 @@
}
}
- private void start(AggregatedPowerStats stats, long timestampMs) {
- for (int i = 0; i < mProcessors.size(); i++) {
- PowerComponentAggregatedPowerStats component =
- stats.getPowerComponentStats(mProcessors.keyAt(i));
- if (component != null) {
- mProcessors.valueAt(i).start(component, timestampMs);
- }
- }
- }
-
- private void finish(AggregatedPowerStats stats, long timestampMs) {
- for (int i = 0; i < mProcessors.size(); i++) {
- PowerComponentAggregatedPowerStats component =
- stats.getPowerComponentStats(mProcessors.keyAt(i));
- if (component != null) {
- mProcessors.valueAt(i).finish(component, timestampMs);
- }
- }
- }
-
/**
* Reset to prepare for a new aggregation session.
*/
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index d442c61..d9f6c1f 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
@@ -61,7 +62,6 @@
private long mLastScheduledUpdateMs = -1;
@GuardedBy("this")
- @SuppressWarnings("unchecked")
private volatile List<Consumer<PowerStats>> mConsumerList = Collections.emptyList();
public PowerStatsCollector(Handler handler, long throttlePeriodMs,
@@ -90,7 +90,6 @@
* Adds a consumer that will receive a callback every time a snapshot of stats is collected.
* The method is thread safe.
*/
- @SuppressWarnings("unchecked")
public void addConsumer(Consumer<PowerStats> consumer) {
synchronized (this) {
if (mConsumerList.contains(consumer)) {
@@ -107,7 +106,6 @@
* Removes a consumer.
* The method is thread safe.
*/
- @SuppressWarnings("unchecked")
public void removeConsumer(Consumer<PowerStats> consumer) {
synchronized (this) {
List<Consumer<PowerStats>> newList = new ArrayList<>(mConsumerList);
@@ -131,18 +129,6 @@
return mEnabled;
}
- @SuppressWarnings("GuardedBy") // Field is volatile
- public void collectAndDeliverStats() {
- PowerStats stats = collectStats();
- if (stats == null) {
- return;
- }
- List<Consumer<PowerStats>> consumerList = mConsumerList;
- for (int i = consumerList.size() - 1; i >= 0; i--) {
- consumerList.get(i).accept(stats);
- }
- }
-
/**
* Schedules a stats snapshot collection, throttled in accordance with the
* {@link #mThrottlePeriodMs} parameter.
@@ -175,8 +161,30 @@
return true;
}
+ /**
+ * Performs a PowerStats collection pass and delivers the result to registered consumers.
+ */
+ @SuppressWarnings("GuardedBy") // Field is volatile
+ public void collectAndDeliverStats() {
+ deliverStats(collectStats());
+ }
+
@Nullable
- protected abstract PowerStats collectStats();
+ protected PowerStats collectStats() {
+ return null;
+ }
+
+ @SuppressWarnings("GuardedBy") // Field is volatile
+ protected void deliverStats(PowerStats stats) {
+ if (stats == null) {
+ return;
+ }
+
+ List<Consumer<PowerStats>> consumerList = mConsumerList;
+ for (int i = consumerList.size() - 1; i >= 0; i--) {
+ consumerList.get(i).accept(stats);
+ }
+ }
/**
* Collects a fresh stats snapshot and prints it to the supplied printer.
@@ -231,10 +239,33 @@
}
interface ConsumedEnergyRetriever {
+ @NonNull
int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType, String name);
+ String getEnergyConsumerName(int energyConsumerId);
+
@Nullable
- long[] getConsumedEnergyUws(int[] energyConsumerIds);
+ EnergyConsumerResult[] getConsumedEnergy(int[] energyConsumerIds);
+
+ @Nullable
+ default long[] getConsumedEnergyUws(int[] energyConsumerIds) {
+ EnergyConsumerResult[] results = getConsumedEnergy(energyConsumerIds);
+ if (results == null) {
+ return null;
+ }
+
+ long[] energy = new long[energyConsumerIds.length];
+ for (int i = 0; i < energyConsumerIds.length; i++) {
+ int id = energyConsumerIds[i];
+ for (EnergyConsumerResult result : results) {
+ if (result.id == id) {
+ energy[i] = result.energyUWs;
+ break;
+ }
+ }
+ }
+ return energy;
+ }
default int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType) {
return getEnergyConsumerIds(energyConsumerType, null);
@@ -243,24 +274,38 @@
static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever {
private final PowerStatsInternal mPowerStatsInternal;
+ private EnergyConsumer[] mEnergyConsumers;
ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal) {
mPowerStatsInternal = powerStatsInternal;
}
- @Override
- public int[] getEnergyConsumerIds(int energyConsumerType, String name) {
- if (mPowerStatsInternal == null) {
- return new int[0];
+ private void ensureEnergyConsumers() {
+ if (mEnergyConsumers != null) {
+ return;
}
- EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
- if (energyConsumerInfo == null) {
+ if (mPowerStatsInternal == null) {
+ mEnergyConsumers = new EnergyConsumer[0];
+ return;
+ }
+
+ mEnergyConsumers = mPowerStatsInternal.getEnergyConsumerInfo();
+ if (mEnergyConsumers == null) {
+ mEnergyConsumers = new EnergyConsumer[0];
+ }
+ }
+
+ @Override
+ public int[] getEnergyConsumerIds(int energyConsumerType, String name) {
+ ensureEnergyConsumers();
+
+ if (mEnergyConsumers.length == 0) {
return new int[0];
}
List<EnergyConsumer> energyConsumers = new ArrayList<>();
- for (EnergyConsumer energyConsumer : energyConsumerInfo) {
+ for (EnergyConsumer energyConsumer : mEnergyConsumers) {
if (energyConsumer.type == energyConsumerType
&& (name == null || name.equals(energyConsumer.name))) {
energyConsumers.add(energyConsumer);
@@ -280,32 +325,50 @@
}
@Override
- public long[] getConsumedEnergyUws(int[] energyConsumerIds) {
+ public EnergyConsumerResult[] getConsumedEnergy(int[] energyConsumerIds) {
CompletableFuture<EnergyConsumerResult[]> future =
mPowerStatsInternal.getEnergyConsumedAsync(energyConsumerIds);
- EnergyConsumerResult[] results = null;
try {
- results = future.get(
- POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS);
+ return future.get(POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e);
}
- if (results == null) {
- return null;
- }
+ return null;
+ }
- long[] energy = new long[energyConsumerIds.length];
- for (int i = 0; i < energyConsumerIds.length; i++) {
- int id = energyConsumerIds[i];
- for (EnergyConsumerResult result : results) {
- if (result.id == id) {
- energy[i] = result.energyUWs;
- break;
- }
+ @Override
+ public String getEnergyConsumerName(int energyConsumerId) {
+ ensureEnergyConsumers();
+
+ for (EnergyConsumer energyConsumer : mEnergyConsumers) {
+ if (energyConsumer.id == energyConsumerId) {
+ return sanitizeCustomPowerComponentName(energyConsumer);
}
}
- return energy;
+
+ Slog.e(TAG, "Unsupported energy consumer ID " + energyConsumerId);
+ return "unsupported";
+ }
+
+ private String sanitizeCustomPowerComponentName(EnergyConsumer energyConsumer) {
+ String name = energyConsumer.name;
+ if (name == null || name.isBlank()) {
+ name = "CUSTOM_" + energyConsumer.id;
+ }
+ int length = name.length();
+ StringBuilder sb = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ char c = name.charAt(i);
+ if (Character.isWhitespace(c)) {
+ sb.append(' ');
+ } else if (Character.isISOControl(c)) {
+ sb.append('_');
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
index f6b198a8..549a97e 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
@@ -115,25 +115,16 @@
private void populateBatteryUsageStatsBuilder(
BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats) {
- AggregatedPowerStatsConfig config = mPowerStatsAggregator.getConfig();
- List<AggregatedPowerStatsConfig.PowerComponent> powerComponents =
- config.getPowerComponentsAggregatedStatsConfigs();
- for (int i = powerComponents.size() - 1; i >= 0; i--) {
- populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats,
- powerComponents.get(i));
+ List<PowerComponentAggregatedPowerStats> powerComponentStats =
+ stats.getPowerComponentStats();
+ for (int i = powerComponentStats.size() - 1; i >= 0; i--) {
+ populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, powerComponentStats.get(i));
}
}
- private void populateBatteryUsageStatsBuilder(
- BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats,
- AggregatedPowerStatsConfig.PowerComponent powerComponent) {
- int powerComponentId = powerComponent.getPowerComponentId();
- PowerComponentAggregatedPowerStats powerComponentStats = stats.getPowerComponentStats(
- powerComponentId);
- if (powerComponentStats == null) {
- return;
- }
-
+ private static void populateBatteryUsageStatsBuilder(
+ BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ PowerComponentAggregatedPowerStats powerComponentStats) {
PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
if (descriptor == null) {
return;
@@ -144,7 +135,8 @@
long[] deviceStats = new long[descriptor.statsArrayLength];
double[] totalPower = new double[1];
- MultiStateStats.States.forEachTrackedStateCombination(powerComponent.getDeviceStateConfig(),
+ MultiStateStats.States.forEachTrackedStateCombination(
+ powerComponentStats.getConfig().getDeviceStateConfig(),
states -> {
if (states[AggregatedPowerStatsConfig.STATE_POWER]
!= AggregatedPowerStatsConfig.POWER_STATE_BATTERY) {
@@ -161,29 +153,37 @@
AggregateBatteryConsumer.Builder deviceScope =
batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- deviceScope.addConsumedPower(powerComponentId,
- totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED);
+ if (descriptor.powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ if (batteryUsageStatsBuilder.isSupportedCustomPowerComponent(
+ descriptor.powerComponentId)) {
+ deviceScope.addConsumedPowerForCustomComponent(descriptor.powerComponentId,
+ totalPower[0]);
+ }
+ } else {
+ deviceScope.addConsumedPower(descriptor.powerComponentId,
+ totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED);
+ }
if (layout.isUidPowerAttributionSupported()) {
- populateUidBatteryConsumers(batteryUsageStatsBuilder, powerComponent,
+ populateUidBatteryConsumers(batteryUsageStatsBuilder,
powerComponentStats, layout);
}
}
private static void populateUidBatteryConsumers(
BatteryUsageStats.Builder batteryUsageStatsBuilder,
- AggregatedPowerStatsConfig.PowerComponent powerComponent,
PowerComponentAggregatedPowerStats powerComponentStats,
PowerStatsLayout layout) {
+ AggregatedPowerStatsConfig.PowerComponent powerComponent = powerComponentStats.getConfig();
int powerComponentId = powerComponent.getPowerComponentId();
PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
long[] uidStats = new long[descriptor.uidStatsArrayLength];
- boolean breakDownByProcState =
- batteryUsageStatsBuilder.isProcessStateDataNeeded()
+ // TODO(b/347101393): add support for per-procstate breakdown for custom energy consumers
+ boolean breakDownByProcState = batteryUsageStatsBuilder.isProcessStateDataNeeded()
&& powerComponent
- .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE]
- .isTracked();
+ .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE].isTracked()
+ && powerComponentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
double[] powerByProcState =
new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
@@ -224,19 +224,27 @@
powerAllProcStates += power;
if (breakDownByProcState
&& procState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
- builder.addConsumedPower(builder.getKey(powerComponentId, procState),
- power, BatteryConsumer.POWER_MODEL_UNDEFINED);
+ builder.addConsumedPower(builder.getKey(powerComponentId, procState), power,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
}
}
- builder.addConsumedPower(powerComponentId, powerAllProcStates,
- BatteryConsumer.POWER_MODEL_UNDEFINED);
+ if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ builder.addConsumedPowerForCustomComponent(powerComponentId, powerAllProcStates);
+ } else {
+ builder.addConsumedPower(powerComponentId, powerAllProcStates,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
+ }
powerAllApps += powerAllProcStates;
}
AggregateBatteryConsumer.Builder allAppsScope =
batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
- allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
- BatteryConsumer.POWER_MODEL_UNDEFINED);
+ if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ allAppsScope.addConsumedPowerForCustomComponent(powerComponentId, powerAllApps);
+ } else {
+ allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
+ BatteryConsumer.POWER_MODEL_UNDEFINED);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
index 9624fd2..62abfc6 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
@@ -32,6 +32,8 @@
private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
private static final String EXTRA_UID_DURATION_POSITION = "ud";
+ private static final String EXTRA_UID_ENERGY_CONSUMERS_POSITION = "ue";
+ private static final String EXTRA_UID_ENERGY_CONSUMERS_COUNT = "uec";
private static final String EXTRA_UID_POWER_POSITION = "up";
protected static final int UNSUPPORTED = -1;
@@ -53,6 +55,8 @@
private int mDeviceEnergyConsumerCount;
private int mDevicePowerEstimatePosition = UNSUPPORTED;
private int mUidDurationPosition = UNSUPPORTED;
+ private int mUidEnergyConsumerPosition = UNSUPPORTED;
+ private int mUidEnergyConsumerCount;
private int mUidPowerEstimatePosition = UNSUPPORTED;
public PowerStatsLayout() {
@@ -244,6 +248,36 @@
}
/**
+ * Declares that the UID stats array has a section capturing EnergyConsumer data from
+ * PowerStatsService.
+ */
+ public void addUidSectionEnergyConsumers(int energyConsumerCount) {
+ mUidEnergyConsumerPosition = addUidSection(energyConsumerCount, "energy",
+ FLAG_OPTIONAL);
+ mUidEnergyConsumerCount = energyConsumerCount;
+ }
+
+ public int getUidEnergyConsumerCount() {
+ return mUidEnergyConsumerCount;
+ }
+
+ /**
+ * Saves the accumulated energy for the specified rail the corresponding
+ * <code>stats</code> element.
+ */
+ public void setUidConsumedEnergy(long[] stats, int index, long energy) {
+ stats[mUidEnergyConsumerPosition + index] = energy;
+ }
+
+ /**
+ * Extracts the EnergyConsumer data from a uid stats array for the specified
+ * EnergyConsumer.
+ */
+ public long getUidConsumedEnergy(long[] stats, int index) {
+ return stats[mUidEnergyConsumerPosition + index];
+ }
+
+ /**
* Converts the supplied mAh power estimate to a long and saves it in the corresponding
* element of <code>stats</code>.
*/
@@ -269,6 +303,8 @@
mDeviceEnergyConsumerCount);
extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
extras.putInt(EXTRA_UID_DURATION_POSITION, mUidDurationPosition);
+ extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION, mUidEnergyConsumerPosition);
+ extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT, mUidEnergyConsumerCount);
extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
@@ -284,6 +320,8 @@
mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
mUidDurationPosition = extras.getInt(EXTRA_UID_DURATION_POSITION);
+ mUidEnergyConsumerPosition = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION);
+ mUidEnergyConsumerCount = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT);
mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index f257e1a..dfc8daa 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -43,7 +43,7 @@
* 2. For each UID, compute the proportion of the combined estimates in each state
* and attribute the corresponding portion of the total power estimate in that state to the UID.
*/
-abstract class PowerStatsProcessor {
+public abstract class PowerStatsProcessor {
private static final String TAG = "PowerStatsProcessor";
private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
diff --git a/services/core/java/com/android/server/timezonedetector/OWNERS b/services/core/java/com/android/server/timezonedetector/OWNERS
index 485a0dd..4220d14 100644
--- a/services/core/java/com/android/server/timezonedetector/OWNERS
+++ b/services/core/java/com/android/server/timezonedetector/OWNERS
@@ -1,8 +1,7 @@
# Bug component: 847766
# This is the main list for platform time / time zone detection maintainers, for this dir and
# ultimately referenced by other OWNERS files for components maintained by the same team.
-nfuller@google.com
+boullanger@google.com
jmorace@google.com
kanyinsola@google.com
-mingaleev@google.com
-narayan@google.com
+mingaleev@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 3138a9e..ddbd809 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1026,12 +1026,7 @@
continue;
}
- final boolean trusted;
- if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) {
- trusted = getUserTrustStateInner(id) == TrustState.TRUSTED;
- } else {
- trusted = aggregateIsTrusted(id);
- }
+ final boolean trusted = getUserTrustStateInner(id) == TrustState.TRUSTED;
boolean showingKeyguard = true;
boolean biometricAuthenticated = false;
boolean currentUserIsUnlocked = false;
@@ -1092,19 +1087,15 @@
private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) {
if (isLocked) {
- if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) {
- // A profile with unified challenge is unlockable not by its own biometrics and
- // trust agents, but rather by those of the parent user. Therefore, when protecting
- // the profile's UnlockedDeviceRequired keys, we must use the parent's list of
- // biometric SIDs and weak unlock methods, not the profile's.
- int authUserId = mLockPatternUtils.isProfileWithUnifiedChallenge(userId)
- ? resolveProfileParent(userId) : userId;
+ // A profile with unified challenge is unlockable not by its own biometrics and
+ // trust agents, but rather by those of the parent user. Therefore, when protecting
+ // the profile's UnlockedDeviceRequired keys, we must use the parent's list of
+ // biometric SIDs and weak unlock methods, not the profile's.
+ int authUserId = mLockPatternUtils.isProfileWithUnifiedChallenge(userId)
+ ? resolveProfileParent(userId) : userId;
- mKeyStoreAuthorization.onDeviceLocked(userId, getBiometricSids(authUserId),
- isWeakUnlockMethodEnabled(authUserId));
- } else {
- mKeyStoreAuthorization.onDeviceLocked(userId, getBiometricSids(userId), false);
- }
+ mKeyStoreAuthorization.onDeviceLocked(userId, getBiometricSids(authUserId),
+ isWeakUnlockMethodEnabled(authUserId));
} else {
// Notify Keystore that the device is now unlocked for the user. Note that for unlocks
// with LSKF, this is redundant with the call from LockSettingsService which provides
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e0a8226..6721893 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -341,10 +341,16 @@
}, UserHandle.ALL, intentFilter, null, null);
}
+ private static boolean hasAlwaysBoundPermission(PackageManager pm, ComponentName component) {
+ return pm.checkPermission(android.Manifest.permission.ALWAYS_BOUND_TV_INPUT,
+ component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
+ }
+
private static boolean hasHardwarePermission(PackageManager pm, ComponentName component) {
return pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE,
component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
}
+
@GuardedBy("mLock")
private void buildTvInputListLocked(int userId, String[] updatedPackages) {
UserState userState = getOrCreateUserStateLocked(userId);
@@ -3526,7 +3532,7 @@
continue;
}
ComponentName component = new ComponentName(si.packageName, si.name);
- if (hasHardwarePermission(pm, component)) {
+ if (!hasAlwaysBoundPermission(pm, component) && hasHardwarePermission(pm, component)) {
updateServiceConnectionLocked(component, userId);
}
}
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 34c90f1..edd2fa9 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -2124,7 +2124,7 @@
@Override
public void setTeletextAppEnabled(IBinder sessionToken, boolean enable, int userId) {
if (DEBUG) {
- Slogf.d(TAG, "setTeletextAppEnabled(enable=%d)", enable);
+ Slogf.d(TAG, "setTeletextAppEnabled(enable=%b)", enable);
}
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
index 9756094..503a726 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -108,9 +108,9 @@
throws CustomizationParserException, IOException {
try {
return loadVibrationsInternal(res, vibratorInfo);
- } catch (VibrationXmlParser.VibrationXmlParserException
- | XmlParserException
- | XmlPullParserException e) {
+ } catch (VibrationXmlParser.ParseFailedException
+ | XmlParserException
+ | XmlPullParserException e) {
throw new CustomizationParserException(
"Error parsing haptic feedback customization file.", e);
}
@@ -121,7 +121,6 @@
Resources res, VibratorInfo vibratorInfo) throws
CustomizationParserException,
IOException,
- VibrationXmlParser.VibrationXmlParserException,
XmlParserException,
XmlPullParserException {
if (!Flags.hapticFeedbackVibrationOemCustomizationEnabled()) {
@@ -172,10 +171,6 @@
ParsedVibration parsedVibration = VibrationXmlParser.parseElement(
parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
- if (parsedVibration == null) {
- throw new CustomizationParserException(
- "Unable to parse vibration element for effect " + effectId);
- }
VibrationEffect effect = parsedVibration.resolve(vibratorInfo);
if (effect != null) {
if (effect.getDuration() == Long.MAX_VALUE) {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 84c37180..6537228 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -69,6 +69,7 @@
CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER),
CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON),
CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED),
+ CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS),
IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS),
IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING),
IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING),
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 3dcc7a6..5c15ccb 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -193,6 +193,27 @@
}
};
+ @VisibleForTesting
+ final AppOpsManager.OnOpChangedInternalListener mAppOpsChangeListener =
+ new AppOpsManager.OnOpChangedInternalListener() {
+ @Override
+ public void onOpChanged(int op, String packageName) {
+ if (op != AppOpsManager.OP_VIBRATE) {
+ return;
+ }
+ synchronized (mLock) {
+ if (shouldCancelAppOpModeChangedLocked(mNextVibration)) {
+ clearNextVibrationLocked(
+ new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_APP_OPS));
+ }
+ if (shouldCancelAppOpModeChangedLocked(mCurrentVibration)) {
+ mCurrentVibration.notifyCancelled(new Vibration.EndInfo(
+ Vibration.Status.CANCELLED_BY_APP_OPS), /* immediate= */ false);
+ }
+ }
+ }
+ };
+
static native long nativeInit(OnSyncedVibrationCompleteListener listener);
static native long nativeGetFinalizer();
@@ -238,6 +259,9 @@
mBatteryStatsService = injector.getBatteryStatsService();
mAppOps = mContext.getSystemService(AppOpsManager.class);
+ if (Flags.cancelByAppops()) {
+ mAppOps.startWatchingMode(AppOpsManager.OP_VIBRATE, null, mAppOpsChangeListener);
+ }
PowerManager pm = context.getSystemService(PowerManager.class);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
@@ -1390,6 +1414,15 @@
}
@GuardedBy("mLock")
+ private boolean shouldCancelAppOpModeChangedLocked(@Nullable VibrationStepConductor conductor) {
+ if (conductor == null) {
+ return false;
+ }
+ return checkAppOpModeLocked(conductor.getVibration().callerInfo)
+ != AppOpsManager.MODE_ALLOWED;
+ }
+
+ @GuardedBy("mLock")
private void onAllVibratorsLocked(Consumer<VibratorController> consumer) {
for (int i = 0; i < mVibrators.size(); i++) {
consumer.accept(mVibrators.valueAt(i));
@@ -2461,9 +2494,6 @@
try {
ParsedVibration parsedVibration =
VibrationXmlParser.parseDocument(new StringReader(xml));
- if (parsedVibration == null) {
- throw new IllegalArgumentException("Error parsing vibration XML " + xml);
- }
VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo();
if (combinedVibratorInfo == null) {
throw new IllegalStateException(
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b89120b..b846947 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -650,9 +650,12 @@
synchronized (mLock) {
if (mLastWallpaper != null) {
WallpaperData targetWallpaper = null;
- if (mLastWallpaper.connection.containsDisplay(displayId)) {
+ if (mLastWallpaper.connection != null &&
+ mLastWallpaper.connection.containsDisplay(displayId)) {
targetWallpaper = mLastWallpaper;
- } else if (mFallbackWallpaper.connection.containsDisplay(displayId)) {
+ } else if (mFallbackWallpaper != null &&
+ mFallbackWallpaper.connection != null &&
+ mFallbackWallpaper.connection.containsDisplay(displayId)) {
targetWallpaper = mFallbackWallpaper;
}
if (targetWallpaper == null) return;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d9dc7ba..6b25c84 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -93,6 +93,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN;
import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS;
import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY;
@@ -158,7 +159,6 @@
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityRecord.State.DESTROYED;
@@ -807,9 +807,10 @@
final LetterboxUiController mLetterboxUiController;
/**
- * The policy for transparent activities
+ * App Compat Facade
*/
- final TransparentPolicy mTransparentPolicy;
+ @NonNull
+ final AppCompatController mAppCompatController;
/**
* The scale to fit at least one side of the activity to its parent. If the activity uses
@@ -1706,7 +1707,7 @@
if (isState(RESUMED)) {
newParent.setResumedActivity(this, "onParentChanged");
}
- mTransparentPolicy.start();
+ mAppCompatController.getTransparentPolicy().start();
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -1908,7 +1909,7 @@
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
- mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t);
+ mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t, getPendingTransaction());
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
@@ -2144,8 +2145,8 @@
// Don't move below setOrientation(info.screenOrientation) since it triggers
// getOverrideOrientation that requires having mLetterboxUiController
// initialised.
- mTransparentPolicy = new TransparentPolicy(this, mWmService.mLetterboxConfiguration);
mLetterboxUiController = new LetterboxUiController(mWmService, this);
+ mAppCompatController = new AppCompatController(mWmService, this);
mCameraCompatControlEnabled = mWmService.mContext.getResources()
.getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
mResolveConfigHint = new TaskFragment.ConfigOverrideHint();
@@ -4505,6 +4506,7 @@
mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
mTaskSupervisor.mStoppingActivities.remove(this);
mLetterboxUiController.destroy();
+ mAppCompatController.getTransparentPolicy().stop();
// Defer removal of this activity when either a child is animating, or app transition is on
// going. App transition animation might be applied on the parent task not on the activity,
@@ -6245,12 +6247,13 @@
return false;
}
- // Check if there are any activities with different UID over the activity that is embedded
- // in untrusted mode. Traverse bottom to top with boundary so that it will only check
- // activities above this activity.
+ // Check if there are any activities with different UID occluding partially the activity
+ // that is embedded in untrusted mode. Traverse bottom to top with boundary so that it will
+ // only check activities above this activity.
final ActivityRecord differentUidOverlayActivity = getTask().getActivity(
- a -> !a.finishing && a.getUid() != getUid(), this /* boundary */,
- false /* includeBoundary */, false /* traverseTopToBottom */);
+ a -> !a.finishing && a.getUid() != getUid() && Rect.intersects(a.getBounds(),
+ getBounds()), this /* boundary */, false /* includeBoundary */,
+ false /* traverseTopToBottom */);
return differentUidOverlayActivity != null;
}
@@ -6563,7 +6566,12 @@
// Schedule an idle timeout in case the app doesn't do it for us.
mTaskSupervisor.scheduleIdleTimeout(this);
- mTaskSupervisor.reportResumedActivityLocked(this);
+ mTaskSupervisor.mStoppingActivities.remove(this);
+ if (getDisplayArea().allResumedActivitiesComplete()) {
+ // Construct the compat environment at a relatively stable state if needed.
+ updateCompatDisplayInsets();
+ mRootWindowContainer.executeAppTransitionForAllDisplay();
+ }
resumeKeyDispatchingLocked();
final Task rootTask = getRootTask();
@@ -6968,14 +6976,11 @@
updateReportedVisibilityLocked();
}
- /**
- * Sets whether something has been visible in the task and returns {@code true} if the state
- * is changed from invisible to visible.
- */
- private boolean setTaskHasBeenVisible() {
+ /** Sets whether something has been visible in the task. */
+ private void setTaskHasBeenVisible() {
final boolean wasTaskVisible = task.getHasBeenVisible();
if (wasTaskVisible) {
- return false;
+ return;
}
if (inTransition()) {
// The deferring will be canceled until transition is ready so it won't dispatch
@@ -6983,20 +6988,22 @@
task.setDeferTaskAppear(true);
}
task.setHasBeenVisible(true);
- return true;
}
void onStartingWindowDrawn() {
- boolean wasTaskVisible = false;
if (task != null) {
mSplashScreenStyleSolidColor = true;
- wasTaskVisible = !setTaskHasBeenVisible();
+ setTaskHasBeenVisible();
}
+ if (mStartingData == null || mStartingData.mIsDisplayed) {
+ return;
+ }
+ mStartingData.mIsDisplayed = true;
// The transition may not be executed if the starting process hasn't attached. But if the
// starting window is drawn, the transition can start earlier. Exclude finishing and bubble
// because it may be a trampoline.
- if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble
+ if (app == null && !finishing && !mLaunchedFromBubble
&& mVisibleRequested && !mDisplayContent.mAppTransition.isReady()
&& !mDisplayContent.mAppTransition.isRunning()
&& mDisplayContent.isNextTransitionForward()) {
@@ -7233,9 +7240,6 @@
isInterestingAndDrawn = true;
}
}
- } else if (mStartingData != null && w.isDrawn()) {
- // The starting window for this container is drawn.
- mStartingData.mIsDisplayed = true;
}
}
@@ -7519,7 +7523,8 @@
* use an icon or solid color splash screen will be made by WmShell.
*/
private boolean shouldUseSolidColorSplashScreen(ActivityRecord sourceRecord,
- boolean startActivity, ActivityOptions options, int resolvedTheme) {
+ boolean startActivity, ActivityOptions options, int resolvedTheme,
+ boolean newTask) {
if (sourceRecord == null && !startActivity) {
// Use simple style if this activity is not top activity. This could happen when adding
// a splash screen window to the warm start activity which is re-create because top is
@@ -7542,21 +7547,19 @@
// Choose the default behavior when neither the ActivityRecord nor the activity theme have
// specified a splash screen style.
-
- if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME || launchedFromUid == Process.SHELL_UID) {
- return false;
- } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
+ if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_SYSTEMUI) {
return true;
} else {
// Need to check sourceRecord in case this activity is launched from a service.
if (sourceRecord == null) {
sourceRecord = searchCandidateLaunchingActivity();
}
-
if (sourceRecord != null) {
- return sourceRecord.mSplashScreenStyleSolidColor;
+ return sourceRecord.mSplashScreenStyleSolidColor; // follow previous activity
+ } else if (mLaunchSourceType == LAUNCH_SOURCE_TYPE_HOME
+ || launchedFromUid == Process.SHELL_UID) {
+ return !newTask; // only show icon for new task
}
-
// Use an icon if the activity was launched from System for the first start.
// Otherwise, must use solid color splash screen.
return mLaunchSourceType != LAUNCH_SOURCE_TYPE_SYSTEM || !startActivity;
@@ -7624,7 +7627,7 @@
splashScreenTheme);
mSplashScreenStyleSolidColor = shouldUseSolidColorSplashScreen(sourceRecord, startActivity,
- startOptions, resolvedTheme);
+ startOptions, resolvedTheme, newTask);
final boolean activityCreated =
mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal();
@@ -8102,13 +8105,13 @@
@Configuration.Orientation
int getRequestedConfigurationOrientation(boolean forDisplay,
@ActivityInfo.ScreenOrientation int requestedOrientation) {
- if (mTransparentPolicy.hasInheritedOrientation()) {
+ if (mAppCompatController.getTransparentPolicy().hasInheritedOrientation()) {
final RootDisplayArea root = getRootDisplayArea();
if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
return reverseConfigurationOrientation(
- mTransparentPolicy.getInheritedOrientation());
+ mAppCompatController.getTransparentPolicy().getInheritedOrientation());
} else {
- return mTransparentPolicy.getInheritedOrientation();
+ return mAppCompatController.getTransparentPolicy().getInheritedOrientation();
}
}
if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) {
@@ -8181,7 +8184,9 @@
}
void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
- if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
+ if (mAppCompatController.getAppCompatOverrides()
+ .getAppCompatOrientationOverrides()
+ .shouldIgnoreRequestedOrientation(requestedOrientation)) {
return;
}
final int originalRelaunchingCount = mPendingRelaunchCount;
@@ -8284,7 +8289,8 @@
*/
@Override
protected int getOverrideOrientation() {
- return mLetterboxUiController.overrideOrientationIfNeeded(super.getOverrideOrientation());
+ return mAppCompatController.getOrientationPolicy()
+ .overrideOrientationIfNeeded(super.getOverrideOrientation());
}
/**
@@ -8324,8 +8330,8 @@
@Nullable
CompatDisplayInsets getCompatDisplayInsets() {
- if (mTransparentPolicy.isRunning()) {
- return mTransparentPolicy.getInheritedCompatDisplayInsets();
+ if (mAppCompatController.getTransparentPolicy().isRunning()) {
+ return mAppCompatController.getTransparentPolicy().getInheritedCompatDisplayInsets();
}
return mCompatDisplayInsets;
}
@@ -8488,7 +8494,7 @@
}
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
- mTransparentPolicy.clearInheritedCompatDisplayInsets();
+ mAppCompatController.getTransparentPolicy().clearInheritedCompatDisplayInsets();
}
@VisibleForTesting
@@ -8675,7 +8681,7 @@
}
@Nullable Rect getParentAppBoundsOverride() {
- return Rect.copyOrNull(mResolveConfigHint.mTmpParentAppBoundsOverride);
+ return Rect.copyOrNull(mResolveConfigHint.mParentAppBoundsOverride);
}
/**
@@ -8821,8 +8827,8 @@
return APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
}
// TODO(b/256564921): Investigate if we need new metrics for translucent activities
- if (mTransparentPolicy.isRunning()) {
- return mTransparentPolicy.getInheritedAppCompatState();
+ if (mAppCompatController.getTransparentPolicy().isRunning()) {
+ return mAppCompatController.getTransparentPolicy().getInheritedAppCompatState();
}
if (mInSizeCompatModeForBounds) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
@@ -8860,7 +8866,7 @@
}
final Rect screenResolvedBounds =
mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
- final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
+ final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final float screenResolvedBoundsWidth = screenResolvedBounds.width();
final float parentAppBoundsWidth = parentAppBounds.width();
@@ -8975,7 +8981,7 @@
// We check if the current activity is transparent. In that case we need to
// recomputeConfiguration of the first opaque activity beneath, to allow a
// proper computation of the new bounds.
- if (!mTransparentPolicy.applyOnOpaqueActivityBelow(
+ if (!mAppCompatController.getTransparentPolicy().applyOnOpaqueActivityBelow(
ActivityRecord::recomputeConfiguration)) {
onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
}
@@ -9269,7 +9275,7 @@
*/
private void resolveAspectRatioRestriction(Configuration newParentConfiguration) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
+ final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
// Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
@@ -9310,7 +9316,7 @@
: newParentConfiguration.windowConfiguration.getBounds();
final Rect containerAppBounds = useResolvedBounds
? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
- : mResolveConfigHint.mTmpParentAppBoundsOverride;
+ : mResolveConfigHint.mParentAppBoundsOverride;
final int requestedOrientation = getRequestedConfigurationOrientation();
final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
@@ -9435,7 +9441,8 @@
void updateSizeCompatScale(Rect resolvedAppBounds, Rect containerAppBounds) {
// Only allow to scale down.
- mSizeCompatScale = mTransparentPolicy.findOpaqueNotFinishingActivityBelow()
+ mSizeCompatScale = mAppCompatController.getTransparentPolicy()
+ .findOpaqueNotFinishingActivityBelow()
.map(activityRecord -> activityRecord.mSizeCompatScale)
.orElseGet(() -> {
final int contentW = resolvedAppBounds.width();
@@ -9448,7 +9455,7 @@
}
private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
- if (mTransparentPolicy.isRunning()) {
+ if (mAppCompatController.getTransparentPolicy().isRunning()) {
// To avoid wrong app behaviour, we decided to disable SCM when a translucent activity
// is letterboxed.
return false;
@@ -9511,7 +9518,7 @@
public Rect getBounds() {
// TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
final Rect superBounds = super.getBounds();
- return mTransparentPolicy.findOpaqueNotFinishingActivityBelow()
+ return mAppCompatController.getTransparentPolicy().findOpaqueNotFinishingActivityBelow()
.map(ActivityRecord::getBounds)
.orElseGet(() -> {
if (mSizeCompatBounds != null) {
@@ -9875,8 +9882,8 @@
* Returns the min aspect ratio of this activity.
*/
float getMinAspectRatio() {
- if (mTransparentPolicy.isRunning()) {
- return mTransparentPolicy.getInheritedMinAspectRatio();
+ if (mAppCompatController.getTransparentPolicy().isRunning()) {
+ return mAppCompatController.getTransparentPolicy().getInheritedMinAspectRatio();
}
if (info.applicationInfo == null) {
return info.getMinAspectRatio();
@@ -9915,6 +9922,11 @@
return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
info.getMinAspectRatio());
}
+
+ if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_SMALL)) {
+ return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE,
+ info.getMinAspectRatio());
+ }
return info.getMinAspectRatio();
}
@@ -9926,8 +9938,8 @@
}
float getMaxAspectRatio() {
- if (mTransparentPolicy.isRunning()) {
- return mTransparentPolicy.getInheritedMaxAspectRatio();
+ if (mAppCompatController.getTransparentPolicy().isRunning()) {
+ return mAppCompatController.getTransparentPolicy().getInheritedMaxAspectRatio();
}
return info.getMaxAspectRatio();
}
@@ -10302,7 +10314,7 @@
if (rootTask != null && rootTask.mTranslucentActivityWaiting == this) {
rootTask.checkTranslucentActivityWaiting(null);
}
- final boolean andResume = shouldBeResumed(null /*activeActivity*/);
+ final boolean andResume = isState(RESUMED) || shouldBeResumed(null /*activeActivity*/);
List<ResultInfo> pendingResults = null;
List<ReferrerIntent> pendingNewIntents = null;
if (andResume) {
@@ -10815,7 +10827,8 @@
proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO,
mLetterboxUiController.shouldOverrideMinAspectRatio());
proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP,
- mLetterboxUiController.shouldIgnoreOrientationRequestLoop());
+ mAppCompatController.getAppCompatOverrides().getAppCompatOrientationOverrides()
+ .shouldIgnoreOrientationRequestLoop());
proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
mLetterboxUiController.shouldOverrideForceResizeApp());
proto.write(SHOULD_ENABLE_USER_ASPECT_RATIO_SETTINGS,
@@ -11147,8 +11160,7 @@
boolean cancel) {
// This override is just for getting metrics. allFinished needs to be checked before
// finish because finish resets all the states.
- final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
- if (syncGroup != null && group != getSyncGroup()) return;
+ if (isDifferentSyncGroup(group)) return;
mLastAllReadyAtSync = allSyncFinished();
super.finishSync(outMergedTransaction, group, cancel);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 0e401eb..a0ef030 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -424,19 +424,13 @@
Intent intent = intents[i];
NeededUriGrants intentGrants = null;
- // Refuse possible leaked file descriptors.
- if (intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
+ intent.prepareToEnterSystemServer();
// Get the flag earlier because the intent may be modified in resolveActivity below.
final boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
- // Remove existing mismatch flag so it can be properly updated later
- intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
-
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid,
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e6d8132..717c399 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -718,13 +718,7 @@
onExecutionStarted();
if (mRequest.intent != null) {
- // Refuse possible leaked file descriptors
- if (mRequest.intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- // Remove existing mismatch flag so it can be properly updated later
- mRequest.intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+ mRequest.intent.prepareToEnterSystemServer();
}
final LaunchingState launchingState;
@@ -1655,7 +1649,8 @@
// activity, so this isn't just deliver-to-top
&& mMovedToTopActivity == null
&& !transitionController.hasOrderChanges()
- && !transitionController.isTransientHide(startedActivityRootTask)) {
+ && !transitionController.isTransientHide(startedActivityRootTask)
+ && !newTransition.hasChanged(mLastStartActivityRecord)) {
// We just delivered to top, so there isn't an actual transition here.
if (!forceTransientTransition) {
newTransition.abort();
@@ -1798,6 +1793,7 @@
activity.destroyIfPossible("Removes redundant singleInstance");
}
}
+ targetTaskTop.mTransitionController.collect(targetTaskTop);
recordTransientLaunchIfNeeded(targetTaskTop);
// Recycle the target task for this launch.
startResult =
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index cfd5300..2109f5d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1318,12 +1318,7 @@
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
enforceNotIsolatedCaller("startActivityIntentSender");
if (fillInIntent != null) {
- // Refuse possible leaked file descriptors
- if (fillInIntent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- // Remove existing mismatch flag so it can be properly updated later
- fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+ fillInIntent.prepareToEnterSystemServer();
}
if (!(target instanceof PendingIntentRecord)) {
@@ -1349,10 +1344,10 @@
@Override
public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent,
Bundle bOptions) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
+ if (intent != null) {
+ intent.prepareToEnterSystemServer();
}
+
SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
synchronized (mGlobalLock) {
@@ -1367,8 +1362,6 @@
return false;
}
intent = new Intent(intent);
- // Remove existing mismatch flag so it can be properly updated later
- intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
// The caller is not allowed to change the data.
intent.setDataAndType(r.intent.getData(), r.intent.getType());
// And we are resetting to find the next component...
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3867d2d..cd5576f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -952,7 +952,7 @@
// Schedule transaction.
if (shouldDispatchLaunchActivityItemIndependently(r.info.packageName, r.getUid())) {
// LaunchActivityItem has @UnsupportedAppUsage usages.
- // Guard the bundleClientTransactionFlag feature with targetSDK on Android 15+.
+ // Guard with targetSDK on Android 15+.
// To not bundle the transaction, dispatch the pending before schedule new
// transaction.
mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread());
@@ -2064,21 +2064,6 @@
}
}
- boolean reportResumedActivityLocked(ActivityRecord r) {
- // A resumed activity cannot be stopping. remove from list
- mStoppingActivities.remove(r);
-
- final Task rootTask = r.getRootTask();
- if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {
- mRootWindowContainer.ensureActivitiesVisible();
- // Make sure activity & window visibility should be identical
- // for all displays in this stage.
- mRootWindowContainer.executeAppTransitionForAllDisplay();
- return true;
- }
- return false;
- }
-
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
final Task task = r.getTask();
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
new file mode 100644
index 0000000..4b0d739
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+
+/**
+ * Allows the interaction with all the app compat policies and configurations
+ */
+class AppCompatController {
+
+ @NonNull
+ private final TransparentPolicy mTransparentPolicy;
+ @NonNull
+ private final AppCompatOrientationPolicy mOrientationPolicy;
+ @NonNull
+ private final AppCompatOverrides mAppCompatOverrides;
+
+ AppCompatController(@NonNull WindowManagerService wmService,
+ @NonNull ActivityRecord activityRecord) {
+ mTransparentPolicy = new TransparentPolicy(activityRecord,
+ wmService.mLetterboxConfiguration);
+ mAppCompatOverrides = new AppCompatOverrides(wmService, activityRecord,
+ wmService.mLetterboxConfiguration);
+ // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with aspectRatio.
+ final LetterboxUiController tmpController = activityRecord.mLetterboxUiController;
+ mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord,
+ mAppCompatOverrides, tmpController::shouldApplyUserFullscreenOverride,
+ tmpController::shouldApplyUserMinAspectRatioOverride,
+ tmpController::isSystemOverrideToFullscreenEnabled);
+ }
+
+ @NonNull
+ TransparentPolicy getTransparentPolicy() {
+ return mTransparentPolicy;
+ }
+
+ @NonNull
+ AppCompatOrientationPolicy getOrientationPolicy() {
+ return mOrientationPolicy;
+ }
+
+ @NonNull
+ AppCompatOverrides getAppCompatOverrides() {
+ return mAppCompatOverrides;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
new file mode 100644
index 0000000..446a75c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
+import static android.content.pm.ActivityInfo.screenOrientationToString;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.AppCompatOverrides.asLazy;
+
+import android.annotation.NonNull;
+import android.content.pm.ActivityInfo;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.utils.OptPropFactory;
+
+import java.util.function.BooleanSupplier;
+import java.util.function.LongSupplier;
+
+/**
+ * Encapsulates all the configurations and overrides about orientation used by
+ * {@link AppCompatOrientationPolicy}.
+ */
+class AppCompatOrientationOverrides {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME
+ ? "AppCompatOrientationOverrides" : TAG_ATM;
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+
+ @NonNull
+ private final OptPropFactory.OptProp mIgnoreRequestedOrientationOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp;
+
+ @NonNull
+ final OrientationOverridesState mOrientationOverridesState;
+
+ AppCompatOrientationOverrides(@NonNull OptPropFactory optPropBuilder,
+ @NonNull LetterboxConfiguration letterboxConfiguration,
+ @NonNull ActivityRecord activityRecord) {
+ mActivityRecord = activityRecord;
+ mOrientationOverridesState = new OrientationOverridesState(mActivityRecord,
+ System::currentTimeMillis);
+ final BooleanSupplier isPolicyForIgnoringRequestedOrientationEnabled = asLazy(
+ letterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled);
+ mIgnoreRequestedOrientationOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION,
+ isPolicyForIgnoringRequestedOrientationEnabled);
+ mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
+ isPolicyForIgnoringRequestedOrientationEnabled);
+ }
+
+ /**
+ * Whether should ignore app requested orientation in response to an app
+ * calling {@link android.app.Activity#setRequestedOrientation}.
+ *
+ * <p>This is needed to avoid getting into {@link android.app.Activity#setRequestedOrientation}
+ * loop when {@link DisplayContent#getIgnoreOrientationRequest} is enabled or device has
+ * landscape natural orientation which app developers don't expect. For example, the loop can
+ * look like this:
+ * <ol>
+ * <li>App sets default orientation to "unspecified" at runtime
+ * <li>App requests to "portrait" after checking some condition (e.g. display rotation).
+ * <li>(2) leads to fullscreen -> letterboxed bounds change and activity relaunch because
+ * app can't handle the corresponding config changes.
+ * <li>Loop goes back to (1)
+ * </ol>
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Flag gating the treatment is enabled
+ * <li>Opt-out component property isn't enabled
+ * <li>Opt-in component property or per-app override are enabled
+ * <li>Activity is relaunched after {@link android.app.Activity#setRequestedOrientation}
+ * call from an app or camera compat force rotation treatment is active for the activity.
+ * <li>Orientation request loop detected and is not letterboxed for fixed orientation
+ * </ul>
+ */
+ boolean shouldIgnoreRequestedOrientation(
+ @ActivityInfo.ScreenOrientation int requestedOrientation) {
+ if (mIgnoreRequestedOrientationOptProp.shouldEnableWithOverrideAndProperty(
+ isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION))) {
+ if (mOrientationOverridesState.mIsRelaunchingAfterRequestedOrientationChanged) {
+ Slog.w(TAG, "Ignoring orientation update to "
+ + screenOrientationToString(requestedOrientation)
+ + " due to relaunching after setRequestedOrientation for "
+ + mActivityRecord);
+ return true;
+ }
+ if (isCameraCompatTreatmentActive()) {
+ Slog.w(TAG, "Ignoring orientation update to "
+ + screenOrientationToString(requestedOrientation)
+ + " due to camera compat treatment for " + mActivityRecord);
+ return true;
+ }
+ }
+
+ if (shouldIgnoreOrientationRequestLoop()) {
+ Slog.w(TAG, "Ignoring orientation update to "
+ + screenOrientationToString(requestedOrientation)
+ + " as orientation request loop was detected for "
+ + mActivityRecord);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Whether an app is calling {@link android.app.Activity#setRequestedOrientation}
+ * in a loop and orientation request should be ignored.
+ *
+ * <p>This should only be called once in response to
+ * {@link android.app.Activity#setRequestedOrientation}. See
+ * {@link #shouldIgnoreRequestedOrientation} for more details.
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Flag gating the treatment is enabled
+ * <li>Opt-out component property isn't enabled
+ * <li>Per-app override is enabled
+ * <li>App has requested orientation more than 2 times within 1-second
+ * timer and activity is not letterboxed for fixed orientation
+ * </ul>
+ */
+ boolean shouldIgnoreOrientationRequestLoop() {
+ final boolean loopDetectionEnabled = isCompatChangeEnabled(
+ OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED);
+ if (!mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp
+ .shouldEnableWithOptInOverrideAndOptOutProperty(loopDetectionEnabled)) {
+ return false;
+ }
+ mOrientationOverridesState.updateOrientationRequestLoopState();
+
+ return mOrientationOverridesState.shouldIgnoreRequestInLoop()
+ && !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio();
+ }
+
+ /**
+ * Sets whether an activity is relaunching after the app has called {@link
+ * android.app.Activity#setRequestedOrientation}.
+ */
+ void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) {
+ mOrientationOverridesState
+ .mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching;
+ }
+
+ boolean getIsRelaunchingAfterRequestedOrientationChanged() {
+ return mOrientationOverridesState.mIsRelaunchingAfterRequestedOrientationChanged;
+ }
+
+ @VisibleForTesting
+ int getSetOrientationRequestCounter() {
+ return mOrientationOverridesState.mSetOrientationRequestCounter;
+ }
+
+ private boolean isCompatChangeEnabled(long overrideChangeId) {
+ return mActivityRecord.info.isChangeEnabled(overrideChangeId);
+ }
+
+ /**
+ * @return {@code true} if the App Compat Camera Policy is active for the current activity.
+ */
+ // TODO(b/346253439): Remove after defining dependency with Camera capabilities.
+ private boolean isCameraCompatTreatmentActive() {
+ DisplayContent displayContent = mActivityRecord.mDisplayContent;
+ if (displayContent == null) {
+ return false;
+ }
+ return displayContent.mDisplayRotationCompatPolicy != null
+ && displayContent.mDisplayRotationCompatPolicy
+ .isTreatmentEnabledForActivity(mActivityRecord);
+ }
+
+ static class OrientationOverridesState {
+ // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
+ final boolean mIsOverrideToNosensorOrientationEnabled;
+ // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
+ final boolean mIsOverrideToPortraitOrientationEnabled;
+ // Corresponds to OVERRIDE_ANY_ORIENTATION
+ final boolean mIsOverrideAnyOrientationEnabled;
+ // Corresponds to OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE
+ final boolean mIsOverrideToReverseLandscapeOrientationEnabled;
+
+ private boolean mIsRelaunchingAfterRequestedOrientationChanged;
+
+ // Used to determine reset of mSetOrientationRequestCounter if next app requested
+ // orientation is after timeout value
+ @VisibleForTesting
+ static final int SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS = 1000;
+ // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request
+ // loop
+ @VisibleForTesting
+ static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2;
+ // Updated when ActivityRecord#setRequestedOrientation is called
+ private long mTimeMsLastSetOrientationRequest = 0;
+ // Counter for ActivityRecord#setRequestedOrientation
+ private int mSetOrientationRequestCounter = 0;
+ @VisibleForTesting
+ LongSupplier mCurrentTimeMillisSupplier;
+
+ OrientationOverridesState(@NonNull ActivityRecord activityRecord,
+ @NonNull LongSupplier currentTimeMillisSupplier) {
+ mCurrentTimeMillisSupplier = currentTimeMillisSupplier;
+ mIsOverrideToNosensorOrientationEnabled =
+ activityRecord.info.isChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR);
+ mIsOverrideToPortraitOrientationEnabled =
+ activityRecord.info.isChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT);
+ mIsOverrideAnyOrientationEnabled =
+ activityRecord.info.isChangeEnabled(OVERRIDE_ANY_ORIENTATION);
+ mIsOverrideToReverseLandscapeOrientationEnabled = activityRecord.info
+ .isChangeEnabled(OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE);
+ }
+
+ /**
+ * @return {@code true} if we should start ignoring orientation in a orientation request
+ * loop.
+ */
+ boolean shouldIgnoreRequestInLoop() {
+ return mSetOrientationRequestCounter >= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
+ }
+
+ /**
+ * Updates the orientation request counter using a specific timeout.
+ */
+ void updateOrientationRequestLoopState() {
+ final long currTimeMs = mCurrentTimeMillisSupplier.getAsLong();
+ final long elapsedTime = currTimeMs - mTimeMsLastSetOrientationRequest;
+ if (elapsedTime < SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS) {
+ mSetOrientationRequestCounter++;
+ } else {
+ mSetOrientationRequestCounter = 0;
+ }
+ mTimeMsLastSetOrientationRequest = currTimeMs;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
new file mode 100644
index 0000000..8e9a9e9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
+import static android.content.pm.ActivityInfo.isFixedOrientation;
+import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
+import static android.content.pm.ActivityInfo.screenOrientationToString;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.content.pm.ActivityInfo;
+import android.util.Slog;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Contains all the logic related to orientation in the context of app compatibility
+ */
+class AppCompatOrientationPolicy {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatOrientationPolicy" : TAG_ATM;
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+
+ @NonNull
+ private final AppCompatOverrides mAppCompatOverrides;
+
+ @NonNull
+ private final BooleanSupplier mShouldApplyUserFullscreenOverride;
+ @NonNull
+ private final BooleanSupplier mShouldApplyUserMinAspectRatioOverride;
+ @NonNull
+ private final BooleanSupplier mIsSystemOverrideToFullscreenEnabled;
+
+ // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with spectRatio component
+ AppCompatOrientationPolicy(@NonNull ActivityRecord activityRecord,
+ @NonNull AppCompatOverrides appCompatOverrides,
+ @NonNull BooleanSupplier shouldApplyUserFullscreenOverride,
+ @NonNull BooleanSupplier shouldApplyUserMinAspectRatioOverride,
+ @NonNull BooleanSupplier isSystemOverrideToFullscreenEnabled) {
+ mActivityRecord = activityRecord;
+ mAppCompatOverrides = appCompatOverrides;
+ mShouldApplyUserFullscreenOverride = shouldApplyUserFullscreenOverride;
+ mShouldApplyUserMinAspectRatioOverride = shouldApplyUserMinAspectRatioOverride;
+ mIsSystemOverrideToFullscreenEnabled = isSystemOverrideToFullscreenEnabled;
+ }
+
+ @ActivityInfo.ScreenOrientation
+ int overrideOrientationIfNeeded(@ActivityInfo.ScreenOrientation int candidate) {
+ final DisplayContent displayContent = mActivityRecord.mDisplayContent;
+ final boolean isIgnoreOrientationRequestEnabled = displayContent != null
+ && displayContent.getIgnoreOrientationRequest();
+ if (mShouldApplyUserFullscreenOverride.getAsBoolean() && isIgnoreOrientationRequestEnabled
+ // Do not override orientation to fullscreen for camera activities.
+ // Fixed-orientation activities are rarely tested in other orientations, and it
+ // often results in sideways or stretched previews. As the camera compat treatment
+ // targets fixed-orientation activities, overriding the orientation disables the
+ // treatment.
+ && !mActivityRecord.isCameraActive()) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate)
+ + " for " + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_USER)
+ + " by user aspect ratio settings.");
+ return SCREEN_ORIENTATION_USER;
+ }
+
+ // In some cases (e.g. Kids app) we need to map the candidate orientation to some other
+ // orientation.
+ candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
+
+ if (mShouldApplyUserMinAspectRatioOverride.getAsBoolean() && (!isFixedOrientation(candidate)
+ || candidate == SCREEN_ORIENTATION_LOCKED)) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate)
+ + " for " + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT)
+ + " by user aspect ratio settings.");
+ return SCREEN_ORIENTATION_PORTRAIT;
+ }
+
+ if (mAppCompatOverrides.isAllowOrientationOverrideOptOut()) {
+ return candidate;
+ }
+
+ if (displayContent != null && mAppCompatOverrides
+ .isOverrideOrientationOnlyForCameraEnabled()
+ && (displayContent.mDisplayRotationCompatPolicy == null
+ || !displayContent.mDisplayRotationCompatPolicy
+ .isActivityEligibleForOrientationOverride(mActivityRecord))) {
+ return candidate;
+ }
+
+ // mUserAspectRatio is always initialized first in shouldApplyUserFullscreenOverride(),
+ // which will always come first before this check as user override > device
+ // manufacturer override.
+ if (mIsSystemOverrideToFullscreenEnabled.getAsBoolean() && isIgnoreOrientationRequestEnabled
+ // Do not override orientation to fullscreen for camera activities.
+ // Fixed-orientation activities are rarely tested in other orientations, and it
+ // often results in sideways or stretched previews. As the camera compat treatment
+ // targets fixed-orientation activities, overriding the orientation disables the
+ // treatment.
+ && !mActivityRecord.isCameraActive()) {
+ Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate)
+ + " for " + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_USER));
+ return SCREEN_ORIENTATION_USER;
+ }
+
+ final AppCompatOrientationOverrides.OrientationOverridesState capabilityState =
+ mAppCompatOverrides.getAppCompatOrientationOverrides()
+ .mOrientationOverridesState;
+
+ if (capabilityState.mIsOverrideToReverseLandscapeOrientationEnabled
+ && (isFixedOrientationLandscape(candidate)
+ || capabilityState.mIsOverrideAnyOrientationEnabled)) {
+ Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_REVERSE_LANDSCAPE));
+ return SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+ }
+
+ if (!capabilityState.mIsOverrideAnyOrientationEnabled && isFixedOrientation(candidate)) {
+ return candidate;
+ }
+
+ if (capabilityState.mIsOverrideToPortraitOrientationEnabled) {
+ Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT));
+ return SCREEN_ORIENTATION_PORTRAIT;
+ }
+
+ if (capabilityState.mIsOverrideToNosensorOrientationEnabled) {
+ Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ + mActivityRecord + " is overridden to "
+ + screenOrientationToString(SCREEN_ORIENTATION_NOSENSOR));
+ return SCREEN_ORIENTATION_NOSENSOR;
+ }
+
+ return candidate;
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
new file mode 100644
index 0000000..794008a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
+import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManager;
+
+import com.android.server.wm.utils.OptPropFactory;
+import com.android.window.flags.Flags;
+
+import java.util.function.BooleanSupplier;
+
+/**
+ * Encapsulate logic related to operations guarded by an app override.
+ */
+public class AppCompatOverrides {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatOverrides" : TAG_ATM;
+
+ @NonNull
+ private final LetterboxConfiguration mLetterboxConfiguration;
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+
+ @NonNull
+ private final OptPropFactory.OptProp mFakeFocusOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mCameraCompatAllowForceRotationOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mCameraCompatAllowRefreshOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mCameraCompatEnableRefreshViaPauseOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mAllowMinAspectRatioOverrideOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mAllowUserAspectRatioOverrideOptProp;
+ @NonNull
+ private final OptPropFactory.OptProp mAllowUserAspectRatioFullscreenOverrideOptProp;
+
+ private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
+
+ AppCompatOverrides(@NonNull WindowManagerService wmService,
+ @NonNull ActivityRecord activityRecord,
+ @NonNull LetterboxConfiguration letterboxConfiguration) {
+ mLetterboxConfiguration = letterboxConfiguration;
+ mActivityRecord = activityRecord;
+ final PackageManager packageManager = wmService.mContext.getPackageManager();
+
+ final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
+ activityRecord.packageName);
+
+ mAppCompatOrientationOverrides =
+ new AppCompatOrientationOverrides(optPropBuilder, mLetterboxConfiguration,
+ mActivityRecord);
+
+ mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
+ mLetterboxConfiguration::isCompatFakeFocusEnabled);
+
+ final BooleanSupplier isCameraCompatTreatmentEnabled = asLazy(
+ mLetterboxConfiguration::isCameraCompatTreatmentEnabled);
+ mCameraCompatAllowForceRotationOptProp = optPropBuilder.create(
+ PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION,
+ isCameraCompatTreatmentEnabled);
+ mCameraCompatAllowRefreshOptProp = optPropBuilder.create(
+ PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH,
+ isCameraCompatTreatmentEnabled);
+ mCameraCompatEnableRefreshViaPauseOptProp = optPropBuilder.create(
+ PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE,
+ isCameraCompatTreatmentEnabled);
+
+ mAllowOrientationOverrideOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+
+ mAllowDisplayOrientationOverrideOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE,
+ () -> mActivityRecord.mDisplayContent != null
+ && mActivityRecord.getTask() != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()
+ && !mActivityRecord.getTask().inMultiWindowMode()
+ && mActivityRecord.mDisplayContent.getNaturalOrientation()
+ == ORIENTATION_LANDSCAPE
+ );
+
+ mAllowMinAspectRatioOverrideOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ mAllowForceResizeOverrideOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+ mAllowUserAspectRatioOverrideOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE,
+ mLetterboxConfiguration::isUserAppAspectRatioSettingsEnabled);
+ mAllowUserAspectRatioFullscreenOverrideOptProp = optPropBuilder.create(
+ PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
+ mLetterboxConfiguration::isUserAppAspectRatioFullscreenEnabled);
+ }
+
+ /**
+ * @return {@code true} if the App Compat Camera Policy is active for the current activity.
+ */
+ boolean isCameraCompatTreatmentActive() {
+ final DisplayContent displayContent = mActivityRecord.mDisplayContent;
+ if (displayContent == null) {
+ return false;
+ }
+ return displayContent.mDisplayRotationCompatPolicy != null
+ && displayContent.mDisplayRotationCompatPolicy
+ .isTreatmentEnabledForActivity(mActivityRecord);
+ }
+
+ @NonNull
+ AppCompatOrientationOverrides getAppCompatOrientationOverrides() {
+ return mAppCompatOrientationOverrides;
+ }
+
+ /**
+ * Whether sending compat fake focus for split screen resumed activities is enabled. Needed
+ * because some game engines wait to get focus before drawing the content of the app which isn't
+ * guaranteed by default in multi-window modes.
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Flag gating the treatment is enabled
+ * <li>Component property is NOT set to false
+ * <li>Component property is set to true or per-app override is enabled
+ * </ul>
+ */
+ boolean shouldSendFakeFocus() {
+ return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty(
+ isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS));
+ }
+
+ /**
+ * Whether activity is eligible for camera compat force rotation treatment. See {@link
+ * DisplayRotationCompatPolicy} for context.
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Flag gating the camera compat treatment is enabled.
+ * <li>Activity isn't opted out by the device manufacturer with override or by the app
+ * developers with the component property.
+ * </ul>
+ */
+ boolean shouldForceRotateForCameraCompat() {
+ return mCameraCompatAllowForceRotationOptProp.shouldEnableWithOptOutOverrideAndProperty(
+ isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION));
+ }
+
+ /**
+ * Whether activity is eligible for activity "refresh" after camera compat force rotation
+ * treatment. See {@link DisplayRotationCompatPolicy} for context.
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Flag gating the camera compat treatment is enabled.
+ * <li>Activity isn't opted out by the device manufacturer with override or by the app
+ * developers with the component property.
+ * </ul>
+ */
+ boolean shouldRefreshActivityForCameraCompat() {
+ return mCameraCompatAllowRefreshOptProp.shouldEnableWithOptOutOverrideAndProperty(
+ isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH));
+ }
+
+ /**
+ * Whether activity should be "refreshed" after the camera compat force rotation treatment
+ * using the "resumed -> paused -> resumed" cycle rather than the "resumed -> ... -> stopped
+ * -> ... -> resumed" cycle. See {@link DisplayRotationCompatPolicy} for context.
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Flag gating the camera compat treatment is enabled.
+ * <li>Activity "refresh" via "resumed -> paused -> resumed" cycle isn't disabled with the
+ * component property by the app developers.
+ * <li>Activity "refresh" via "resumed -> paused -> resumed" cycle is enabled by the device
+ * manufacturer with override / by the app developers with the component property.
+ * </ul>
+ */
+ boolean shouldRefreshActivityViaPauseForCameraCompat() {
+ return mCameraCompatEnableRefreshViaPauseOptProp.shouldEnableWithOverrideAndProperty(
+ isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE));
+ }
+
+ boolean isSystemOverrideToFullscreenEnabled(int userAspectRatio) {
+ return isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER)
+ && !mAllowOrientationOverrideOptProp.isFalse()
+ && (userAspectRatio == USER_MIN_ASPECT_RATIO_UNSET
+ || userAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ }
+
+ boolean isAllowOrientationOverrideOptOut() {
+ return mAllowOrientationOverrideOptProp.isFalse();
+ }
+
+ /**
+ * Whether we should apply the min aspect ratio per-app override. When this override is applied
+ * the min aspect ratio given in the app's manifest will be overridden to the largest enabled
+ * aspect ratio treatment unless the app's manifest value is higher. The treatment will also
+ * apply if no value is provided in the manifest.
+ *
+ * <p>This method returns {@code true} when the following conditions are met:
+ * <ul>
+ * <li>Opt-out component property isn't enabled
+ * <li>Per-app override is enabled
+ * </ul>
+ */
+ boolean shouldOverrideMinAspectRatio() {
+ return mAllowMinAspectRatioOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
+ isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO));
+ }
+
+ boolean isOverrideRespectRequestedOrientationEnabled() {
+ return isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
+ }
+
+ boolean isOverrideOrientationOnlyForCameraEnabled() {
+ return isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
+ }
+
+ /**
+ * Whether activity is eligible for camera compatibility free-form treatment.
+ *
+ * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
+ * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
+ * provides changes to the camera and display orientation signals to match those expected on a
+ * portrait device in that orientation (for example, on a standard phone).
+ *
+ * <p>The treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Property gating the camera compatibility free-form treatment is enabled.
+ * <li>Activity isn't opted out by the device manufacturer with override.
+ * </ul>
+ */
+ boolean shouldApplyFreeformTreatmentForCameraCompat() {
+ return Flags.cameraCompatForFreeform() && !isCompatChangeEnabled(
+ OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
+ }
+
+
+ /**
+ * Whether we should apply the min aspect ratio per-app override only when an app is connected
+ * to the camera.
+ * When this override is applied the min aspect ratio given in the app's manifest will be
+ * overridden to the largest enabled aspect ratio treatment unless the app's manifest value
+ * is higher. The treatment will also apply if no value is provided in the manifest.
+ *
+ * <p>This method returns {@code true} when the following conditions are met:
+ * <ul>
+ * <li>Opt-out component property isn't enabled
+ * <li>Per-app override is enabled
+ * </ul>
+ */
+ boolean shouldOverrideMinAspectRatioForCamera() {
+ return mActivityRecord.isCameraActive()
+ && mAllowMinAspectRatioOverrideOptProp
+ .shouldEnableWithOptInOverrideAndOptOutProperty(
+ isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
+ }
+
+ /**
+ * Whether should fix display orientation to landscape natural orientation when a task is
+ * fullscreen and the display is ignoring orientation requests.
+ *
+ * <p>This treatment is enabled when the following conditions are met:
+ * <ul>
+ * <li>Opt-out component property isn't enabled
+ * <li>Opt-in per-app override is enabled
+ * <li>Task is in fullscreen.
+ * <li>{@link DisplayContent#getIgnoreOrientationRequest} is enabled
+ * <li>Natural orientation of the display is landscape.
+ * </ul>
+ */
+ boolean shouldUseDisplayLandscapeNaturalOrientation() {
+ return mAllowDisplayOrientationOverrideOptProp
+ .shouldEnableWithOptInOverrideAndOptOutProperty(
+ isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION));
+ }
+
+ /**
+ * Whether we should apply the force resize per-app override. When this override is applied it
+ * forces the packages it is applied to to be resizable. It won't change whether the app can be
+ * put into multi-windowing mode, but allow the app to resize without going into size-compat
+ * mode when the window container resizes, such as display size change or screen rotation.
+ *
+ * <p>This method returns {@code true} when the following conditions are met:
+ * <ul>
+ * <li>Opt-out component property isn't enabled
+ * <li>Per-app override is enabled
+ * </ul>
+ */
+ boolean shouldOverrideForceResizeApp() {
+ return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
+ isCompatChangeEnabled(FORCE_RESIZE_APP));
+ }
+
+ /**
+ * Whether we should enable users to resize the current app.
+ */
+ boolean shouldEnableUserAspectRatioSettings() {
+ // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+ // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+ // the current app doesn't opt-out so the first part of the predicate is true.
+ return !mAllowUserAspectRatioOverrideOptProp.isFalse()
+ && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && mActivityRecord.mDisplayContent != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ boolean isUserFullscreenOverrideEnabled() {
+ if (mAllowUserAspectRatioOverrideOptProp.isFalse()
+ || mAllowUserAspectRatioFullscreenOverrideOptProp.isFalse()
+ || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Whether we should apply the force non resize per-app override. When this override is applied
+ * it forces the packages it is applied to to be non-resizable.
+ *
+ * <p>This method returns {@code true} when the following conditions are met:
+ * <ul>
+ * <li>Opt-out component property isn't enabled
+ * <li>Per-app override is enabled
+ * </ul>
+ */
+ boolean shouldOverrideForceNonResizeApp() {
+ return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
+ isCompatChangeEnabled(FORCE_NON_RESIZE_APP));
+ }
+
+ private boolean isCompatChangeEnabled(long overrideChangeId) {
+ return mActivityRecord.info.isChangeEnabled(overrideChangeId);
+ }
+
+ @NonNull
+ static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) {
+ return new BooleanSupplier() {
+ private boolean mRead;
+ private boolean mValue;
+
+ @Override
+ public boolean getAsBoolean() {
+ if (!mRead) {
+ mRead = true;
+ mValue = supplier.getAsBoolean();
+ }
+ return mValue;
+ }
+ };
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 939babc..c55a100 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -256,7 +256,8 @@
tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
if (mDisplayContent.mAtmService.mBackNavigationController
.removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
- mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations();
+ mDisplayContent.mAtmService.mBackNavigationController
+ .clearBackAnimations(false /* cancel */);
}
}
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index e8faff6..a8cc2ae 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -348,6 +348,11 @@
wc.setSyncGroup(this);
}
wc.prepareSync();
+ if (wc.mSyncState == WindowContainer.SYNC_STATE_NONE && wc.mSyncGroup != null) {
+ Slog.w(TAG, "addToSync: unset SyncGroup " + wc.mSyncGroup.mSyncId
+ + " for non-sync " + wc);
+ wc.mSyncGroup = null;
+ }
if (mReady) {
mWm.mWindowPlacerLocked.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 1ce324f..14ae918 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -252,7 +252,8 @@
// skip if one of participant activity is translucent
backType = BackNavigationInfo.TYPE_CALLBACK;
} else if (prevActivities.size() > 0) {
- if (!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities)) {
+ if ((!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities))
+ && isAllActivitiesCreated(prevActivities)) {
// We have another Activity in the same currentTask to go to
final WindowContainer parent = currentActivity.getParent();
final boolean canCustomize = parent != null
@@ -549,6 +550,17 @@
return !prevActivities.isEmpty();
}
+ private static boolean isAllActivitiesCreated(
+ @NonNull ArrayList<ActivityRecord> prevActivities) {
+ for (int i = prevActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord check = prevActivities.get(i);
+ if (check.isState(ActivityRecord.State.INITIALIZING)) {
+ return false;
+ }
+ }
+ return !prevActivities.isEmpty();
+ }
+
boolean isMonitoringTransition() {
return mAnimationHandler.mComposed || mNavigationMonitor.isMonitorForRemote();
}
@@ -739,7 +751,7 @@
mObserver.sendResult(null /* result */);
}
if (isMonitorAnimationOrTransition()) {
- clearBackAnimations();
+ clearBackAnimations(true /* cancel */);
}
cancelPendingAnimation();
}
@@ -831,24 +843,23 @@
* Cleanup animation, this can either happen when legacy transition ready, or when the Shell
* transition finish.
*/
- void clearBackAnimations() {
- mAnimationHandler.clearBackAnimateTarget();
+ void clearBackAnimations(boolean cancel) {
+ mAnimationHandler.clearBackAnimateTarget(cancel);
mNavigationMonitor.stopMonitorTransition();
mWaitTransitionFinish = null;
}
/**
- * Called when a transition finished.
- * Handle the pending animation when the running transition finished.
+ * Handle the pending animation when the running transition finished, all the visibility change
+ * has applied so ready to start pending predictive back animation.
* @param targets The final animation targets derived in transition.
* @param finishedTransition The finished transition target.
*/
void onTransitionFinish(ArrayList<Transition.ChangeInfo> targets,
@NonNull Transition finishedTransition) {
if (finishedTransition == mWaitTransitionFinish) {
- clearBackAnimations();
+ clearBackAnimations(false /* cancel */);
}
-
if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) {
return;
}
@@ -982,7 +993,7 @@
mCloseAdaptor = createAdaptor(close, false, mSwitchType);
if (mCloseAdaptor.mAnimationTarget == null) {
Slog.w(TAG, "composeNewAnimations fail, skip");
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
return;
}
@@ -1001,7 +1012,7 @@
mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open);
if (!mOpenAnimAdaptor.isValid()) {
Slog.w(TAG, "compose animations fail, skip");
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
return;
}
mOpenActivities = openingActivities;
@@ -1013,7 +1024,7 @@
Slog.e(TAG, "Previous animation is running " + this);
return false;
}
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
if (close == null || open == null || open.length == 0 || open.length > 2) {
Slog.e(TAG, "reset animation with null target close: "
+ close + " open: " + Arrays.toString(open));
@@ -1102,7 +1113,19 @@
return false;
}
- void finishPresentAnimations() {
+ void finishPresentAnimations(boolean cancel) {
+ if (mOpenActivities != null) {
+ for (int i = mOpenActivities.length - 1; i >= 0; --i) {
+ final ActivityRecord resetActivity = mOpenActivities[i];
+ if (resetActivity.mDisplayContent.isFixedRotationLaunchingApp(resetActivity)) {
+ resetActivity.mDisplayContent
+ .continueUpdateOrientationForDiffOrienLaunchingApp();
+ }
+ if (resetActivity.mLaunchTaskBehind) {
+ restoreLaunchBehind(resetActivity, cancel);
+ }
+ }
+ }
if (mCloseAdaptor != null) {
mCloseAdaptor.mTarget.cancelAnimation();
mCloseAdaptor = null;
@@ -1111,15 +1134,6 @@
mOpenAnimAdaptor.cleanUp(mStartingSurfaceTargetMatch);
mOpenAnimAdaptor = null;
}
-
- if (mOpenActivities != null) {
- for (int i = mOpenActivities.length - 1; i >= 0; --i) {
- final ActivityRecord resetActivity = mOpenActivities[i];
- if (resetActivity.mLaunchTaskBehind) {
- restoreLaunchBehind(resetActivity);
- }
- }
- }
}
void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) {
@@ -1130,10 +1144,10 @@
mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction);
}
- void clearBackAnimateTarget() {
+ void clearBackAnimateTarget(boolean cancel) {
if (mComposed) {
mComposed = false;
- finishPresentAnimations();
+ finishPresentAnimations(cancel);
}
mWaitTransition = false;
mStartingSurfaceTargetMatch = false;
@@ -1649,7 +1663,7 @@
return;
}
if (!triggerBack) {
- clearBackAnimateTarget();
+ clearBackAnimateTarget(true /* cancel */);
} else {
mWaitTransition = true;
}
@@ -1732,25 +1746,23 @@
true /* notifyClients */);
}
- private static void restoreLaunchBehind(@NonNull ActivityRecord activity) {
+ private static void restoreLaunchBehind(@NonNull ActivityRecord activity, boolean cancel) {
if (!activity.isAttached()) {
// The activity was detached from hierarchy.
return;
}
-
- if (activity.mDisplayContent.isFixedRotationLaunchingApp(activity)) {
- activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
- }
-
- // Restore the launch-behind state.
- activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
activity.mLaunchTaskBehind = false;
- // Ignore all change
- activity.mTransitionController.mSnapshotController
- .mActivitySnapshotController.clearOnBackPressedActivities();
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
activity);
+ if (cancel) {
+ // Restore the launch-behind state
+ // TODO b/347168362 Change status directly during collecting for a transition.
+ activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
+ // Ignore all change
+ activity.mTransitionController.mSnapshotController
+ .mActivitySnapshotController.clearOnBackPressedActivities();
+ }
}
void checkAnimationReady(WallpaperController wallpaperController) {
@@ -1770,7 +1782,7 @@
if (!mBackAnimationInProgress) {
// gesture is already finished, do not start animation
if (mPendingAnimation != null) {
- clearBackAnimations();
+ clearBackAnimations(true /* cancel */);
mPendingAnimation = null;
}
return;
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index e396f2b..bf34759 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -35,7 +35,6 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
/**
* Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
@@ -72,9 +71,8 @@
* @throws RemoteException
*
* @see ClientTransaction
- * @deprecated use {@link #scheduleTransactionItem(IApplicationThread, ClientTransactionItem)}.
*/
- @Deprecated
+ @VisibleForTesting
void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
try {
@@ -114,22 +112,13 @@
*/
void scheduleTransactionItem(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
- // The behavior is different depending on the flag.
- // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
- // dispatch all pending transactions at once.
- if (Flags.bundleClientTransactionFlag()) {
- final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
- clientTransaction.addTransactionItem(transactionItem);
+ // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
+ // transactions at once.
+ final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
+ clientTransaction.addTransactionItem(transactionItem);
- onClientTransactionItemScheduled(clientTransaction,
- false /* shouldDispatchImmediately */);
- } else {
- // TODO(b/260873529): cleanup after launch.
- final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- clientTransaction.addTransactionItem(transactionItem);
-
- scheduleTransaction(clientTransaction);
- }
+ onClientTransactionItemScheduled(clientTransaction,
+ false /* shouldDispatchImmediately */);
}
void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client,
@@ -156,27 +145,18 @@
@NonNull ClientTransactionItem transactionItem,
@NonNull ActivityLifecycleItem lifecycleItem,
boolean shouldDispatchImmediately) throws RemoteException {
- // The behavior is different depending on the flag.
- // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
- // dispatch all pending transactions at once.
- if (Flags.bundleClientTransactionFlag()) {
- final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
- clientTransaction.addTransactionItem(transactionItem);
- clientTransaction.addTransactionItem(lifecycleItem);
+ // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
+ // transactions at once.
+ final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
+ clientTransaction.addTransactionItem(transactionItem);
+ clientTransaction.addTransactionItem(lifecycleItem);
- onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
- } else {
- // TODO(b/260873529): cleanup after launch.
- final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- clientTransaction.addTransactionItem(transactionItem);
- clientTransaction.addTransactionItem(lifecycleItem);
- scheduleTransaction(clientTransaction);
- }
+ onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
}
/** Executes all the pending transactions. */
void dispatchPendingTransactions() {
- if (!Flags.bundleClientTransactionFlag() || mPendingTransactions.isEmpty()) {
+ if (mPendingTransactions.isEmpty()) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionsDispatched");
@@ -196,9 +176,6 @@
/** Executes the pending transaction for the given client process. */
void dispatchPendingTransaction(@NonNull IApplicationThread client) {
- if (!Flags.bundleClientTransactionFlag()) {
- return;
- }
final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
if (pendingTransaction != null) {
try {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ad711cb..7ffffe9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5047,9 +5047,6 @@
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- if (!com.android.window.flags.Flags.removePrepareSurfaceInPlacement()) {
- prepareSurfaces();
- }
// This should be called after the insets have been dispatched to clients and we have
// committed finish drawing windows.
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 362d4ef..2aa7c0c 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -20,6 +20,7 @@
import static android.view.SurfaceControl.HIDDEN;
import static android.window.TaskConstants.TASK_CHILD_LAYER_LETTERBOX_BACKGROUND;
+import android.annotation.NonNull;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -209,16 +210,18 @@
return false;
}
- public void applySurfaceChanges(SurfaceControl.Transaction t) {
+ /** Applies surface changes such as colour, window crop, position and input info. */
+ public void applySurfaceChanges(@NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction inputT) {
if (useFullWindowSurface()) {
- mFullWindowSurface.applySurfaceChanges(t);
+ mFullWindowSurface.applySurfaceChanges(t, inputT);
for (LetterboxSurface surface : mSurfaces) {
surface.remove();
}
} else {
for (LetterboxSurface surface : mSurfaces) {
- surface.applySurfaceChanges(t);
+ surface.applySurfaceChanges(t, inputT);
}
mFullWindowSurface.remove();
@@ -418,7 +421,8 @@
return Math.max(0, mLayoutFrameGlobal.height());
}
- public void applySurfaceChanges(SurfaceControl.Transaction t) {
+ public void applySurfaceChanges(@NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction inputT) {
if (!needsApplySurfaceChanges()) {
// Nothing changed.
return;
@@ -446,7 +450,7 @@
}
if (mSurface != null && mInputInterceptor != null) {
mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative);
- t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
+ inputT.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 14a0467..85eeab1 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -18,33 +18,7 @@
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
-import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
-import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
-import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
-import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
-import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
-import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
-import static android.content.pm.ActivityInfo.isFixedOrientation;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
-import static android.content.pm.ActivityInfo.screenOrientationToString;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
@@ -53,22 +27,9 @@
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
-import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
-import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
@@ -105,7 +66,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager.TaskDescription;
import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
-import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -126,76 +86,25 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
-import com.android.server.wm.utils.OptPropFactory;
-import com.android.server.wm.utils.OptPropFactory.OptProp;
import com.android.window.flags.Flags;
import java.io.PrintWriter;
-import java.util.function.BooleanSupplier;
/** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
// TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
// SizeCompatTests and LetterboxTests but not all.
-// TODO(b/185264020): Consider making LetterboxUiController applicable to any level of the
-// hierarchy in addition to ActivityRecord (Task, DisplayArea, ...).
-// TODO(b/263021211): Consider renaming to more generic CompatUIController.
final class LetterboxUiController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
- // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request loop
- @VisibleForTesting
- static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2;
- // Used to determine reset of mSetOrientationRequestCounter if next app requested
- // orientation is after timeout value
- @VisibleForTesting
- static final int SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS = 1000;
-
private final Point mTmpPoint = new Point();
private final LetterboxConfiguration mLetterboxConfiguration;
private final ActivityRecord mActivityRecord;
- // TODO(b/265576778): Cache other overrides as well.
-
- // Corresponds to OVERRIDE_ANY_ORIENTATION
- private final boolean mIsOverrideAnyOrientationEnabled;
- // Corresponds to OVERRIDE_ANY_ORIENTATION_TO_USER
- private final boolean mIsSystemOverrideToFullscreenEnabled;
- // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
- private final boolean mIsOverrideToPortraitOrientationEnabled;
- // Corresponds to OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR
- private final boolean mIsOverrideToNosensorOrientationEnabled;
- // Corresponds to OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE
- private final boolean mIsOverrideToReverseLandscapeOrientationEnabled;
- // Corresponds to OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA
- private final boolean mIsOverrideOrientationOnlyForCameraEnabled;
- // Corresponds to OVERRIDE_RESPECT_REQUESTED_ORIENTATION
- private final boolean mIsOverrideRespectRequestedOrientationEnabled;
-
- @NonNull
- private final OptProp mAllowOrientationOverrideOptProp;
- @NonNull
- private final OptProp mAllowDisplayOrientationOverrideOptProp;
- @NonNull
- private final OptProp mAllowMinAspectRatioOverrideOptProp;
- @NonNull
- private final OptProp mAllowForceResizeOverrideOptProp;
-
- @NonNull
- private final OptProp mAllowUserAspectRatioOverrideOptProp;
- @NonNull
- private final OptProp mAllowUserAspectRatioFullscreenOverrideOptProp;
-
private boolean mShowWallpaperForLetterboxBackground;
- // Updated when ActivityRecord#setRequestedOrientation is called
- private long mTimeMsLastSetOrientationRequest = 0;
-
- // Counter for ActivityRecord#setRequestedOrientation
- private int mSetOrientationRequestCounter = 0;
-
// TODO(b/315140179): Make mUserAspectRatio final
// The min aspect ratio override set by user
@PackageManager.UserMinAspectRatio
@@ -204,31 +113,11 @@
@Nullable
private Letterbox mLetterbox;
- @NonNull
- private final OptProp mCameraCompatAllowForceRotationOptProp;
-
- @NonNull
- private final OptProp mCameraCompatAllowRefreshOptProp;
-
- @NonNull
- private final OptProp mCameraCompatEnableRefreshViaPauseOptProp;
-
// Whether activity "refresh" was requested but not finished in
// ActivityRecord#activityResumedLocked following the camera compat force rotation in
// DisplayRotationCompatPolicy.
private boolean mIsRefreshRequested;
- @NonNull
- private final OptProp mIgnoreRequestedOrientationOptProp;
-
- @NonNull
- private final OptProp mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp;
-
- @NonNull
- private final OptProp mFakeFocusOptProp;
-
- private boolean mIsRelaunchingAfterRequestedOrientationChanged;
-
private boolean mLastShouldShowLetterboxUi;
private boolean mDoubleTapEvent;
@@ -242,76 +131,6 @@
// is created in its constructor. It shouldn't be used in this constructor but it's safe
// to use it after since controller is only used in ActivityRecord.
mActivityRecord = activityRecord;
-
- PackageManager packageManager = wmService.mContext.getPackageManager();
-
- final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
- activityRecord.packageName);
-
- final BooleanSupplier isPolicyForIgnoringRequestedOrientationEnabled = asLazy(
- mLetterboxConfiguration::isPolicyForIgnoringRequestedOrientationEnabled);
- mIgnoreRequestedOrientationOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION,
- isPolicyForIgnoringRequestedOrientationEnabled);
- mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
- isPolicyForIgnoringRequestedOrientationEnabled);
-
- mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS,
- mLetterboxConfiguration::isCompatFakeFocusEnabled);
-
- final BooleanSupplier isCameraCompatTreatmentEnabled = asLazy(
- mLetterboxConfiguration::isCameraCompatTreatmentEnabled);
- mCameraCompatAllowForceRotationOptProp = optPropBuilder.create(
- PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION,
- isCameraCompatTreatmentEnabled);
- mCameraCompatAllowRefreshOptProp = optPropBuilder.create(
- PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH,
- isCameraCompatTreatmentEnabled);
- mCameraCompatEnableRefreshViaPauseOptProp = optPropBuilder.create(
- PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE,
- isCameraCompatTreatmentEnabled);
-
- mAllowOrientationOverrideOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
-
- mAllowDisplayOrientationOverrideOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE,
- () -> mActivityRecord.mDisplayContent != null
- && mActivityRecord.getTask() != null
- && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()
- && !mActivityRecord.getTask().inMultiWindowMode()
- && mActivityRecord.mDisplayContent.getNaturalOrientation()
- == ORIENTATION_LANDSCAPE
- );
-
- mAllowMinAspectRatioOverrideOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
-
- mAllowForceResizeOverrideOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
-
- mAllowUserAspectRatioOverrideOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE,
- mLetterboxConfiguration::isUserAppAspectRatioSettingsEnabled);
-
- mAllowUserAspectRatioFullscreenOverrideOptProp = optPropBuilder.create(
- PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
- mLetterboxConfiguration::isUserAppAspectRatioFullscreenEnabled);
-
- mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
- mIsSystemOverrideToFullscreenEnabled =
- isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION_TO_USER);
- mIsOverrideToPortraitOrientationEnabled =
- isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT);
- mIsOverrideToReverseLandscapeOrientationEnabled =
- isCompatChangeEnabled(OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE);
- mIsOverrideToNosensorOrientationEnabled =
- isCompatChangeEnabled(OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR);
- mIsOverrideOrientationOnlyForCameraEnabled =
- isCompatChangeEnabled(OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA);
- mIsOverrideRespectRequestedOrientationEnabled =
- isCompatChangeEnabled(OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
}
/** Cleans up {@link Letterbox} if it exists.*/
@@ -320,7 +139,6 @@
mLetterbox.destroy();
mLetterbox = null;
}
- mActivityRecord.mTransparentPolicy.stop();
}
void onMovedToDisplay(int displayId) {
@@ -329,103 +147,11 @@
}
}
- /**
- * Whether should ignore app requested orientation in response to an app
- * calling {@link android.app.Activity#setRequestedOrientation}.
- *
- * <p>This is needed to avoid getting into {@link android.app.Activity#setRequestedOrientation}
- * loop when {@link DisplayContent#getIgnoreOrientationRequest} is enabled or device has
- * landscape natural orientation which app developers don't expect. For example, the loop can
- * look like this:
- * <ol>
- * <li>App sets default orientation to "unspecified" at runtime
- * <li>App requests to "portrait" after checking some condition (e.g. display rotation).
- * <li>(2) leads to fullscreen -> letterboxed bounds change and activity relaunch because
- * app can't handle the corresponding config changes.
- * <li>Loop goes back to (1)
- * </ol>
- *
- * <p>This treatment is enabled when the following conditions are met:
- * <ul>
- * <li>Flag gating the treatment is enabled
- * <li>Opt-out component property isn't enabled
- * <li>Opt-in component property or per-app override are enabled
- * <li>Activity is relaunched after {@link android.app.Activity#setRequestedOrientation}
- * call from an app or camera compat force rotation treatment is active for the activity.
- * <li>Orientation request loop detected and is not letterboxed for fixed orientation
- * </ul>
- */
- boolean shouldIgnoreRequestedOrientation(@ScreenOrientation int requestedOrientation) {
- if (mIgnoreRequestedOrientationOptProp.shouldEnableWithOverrideAndProperty(
- isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION))) {
- if (mIsRelaunchingAfterRequestedOrientationChanged) {
- Slog.w(TAG, "Ignoring orientation update to "
- + screenOrientationToString(requestedOrientation)
- + " due to relaunching after setRequestedOrientation for "
- + mActivityRecord);
- return true;
- }
- if (isCameraCompatTreatmentActive()) {
- Slog.w(TAG, "Ignoring orientation update to "
- + screenOrientationToString(requestedOrientation)
- + " due to camera compat treatment for " + mActivityRecord);
- return true;
- }
- }
-
- if (shouldIgnoreOrientationRequestLoop()) {
- Slog.w(TAG, "Ignoring orientation update to "
- + screenOrientationToString(requestedOrientation)
- + " as orientation request loop was detected for "
- + mActivityRecord);
- return true;
- }
- return false;
- }
-
- /**
- * Whether an app is calling {@link android.app.Activity#setRequestedOrientation}
- * in a loop and orientation request should be ignored.
- *
- * <p>This should only be called once in response to
- * {@link android.app.Activity#setRequestedOrientation}. See
- * {@link #shouldIgnoreRequestedOrientation} for more details.
- *
- * <p>This treatment is enabled when the following conditions are met:
- * <ul>
- * <li>Flag gating the treatment is enabled
- * <li>Opt-out component property isn't enabled
- * <li>Per-app override is enabled
- * <li>App has requested orientation more than 2 times within 1-second
- * timer and activity is not letterboxed for fixed orientation
- * </ul>
- */
- boolean shouldIgnoreOrientationRequestLoop() {
- final boolean loopDetectionEnabled = isCompatChangeEnabled(
- OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED);
- if (!mAllowIgnoringOrientationRequestWhenLoopDetectedOptProp
- .shouldEnableWithOptInOverrideAndOptOutProperty(loopDetectionEnabled)) {
- return false;
- }
-
- final long currTimeMs = System.currentTimeMillis();
- if (currTimeMs - mTimeMsLastSetOrientationRequest
- < SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS) {
- mSetOrientationRequestCounter += 1;
- } else {
- // Resets app setOrientationRequest counter if timed out
- mSetOrientationRequestCounter = 0;
- }
- // Update time last called
- mTimeMsLastSetOrientationRequest = currTimeMs;
-
- return mSetOrientationRequestCounter >= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP
- && !mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio();
- }
@VisibleForTesting
int getSetOrientationRequestCounter() {
- return mSetOrientationRequestCounter;
+ return getAppCompatOverrides().getAppCompatOrientationOverrides()
+ .getSetOrientationRequestCounter();
}
/**
@@ -441,8 +167,7 @@
* </ul>
*/
boolean shouldSendFakeFocus() {
- return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty(
- isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS));
+ return getAppCompatOverrides().shouldSendFakeFocus();
}
/**
@@ -458,8 +183,7 @@
* </ul>
*/
boolean shouldOverrideMinAspectRatio() {
- return mAllowMinAspectRatioOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
- isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO));
+ return getAppCompatOverrides().shouldOverrideMinAspectRatio();
}
/**
@@ -476,10 +200,7 @@
* </ul>
*/
boolean shouldOverrideMinAspectRatioForCamera() {
- return mActivityRecord.isCameraActive()
- && mAllowMinAspectRatioOverrideOptProp
- .shouldEnableWithOptInOverrideAndOptOutProperty(
- isCompatChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA));
+ return getAppCompatOverrides().shouldOverrideMinAspectRatioForCamera();
}
/**
@@ -495,8 +216,7 @@
* </ul>
*/
boolean shouldOverrideForceResizeApp() {
- return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
- isCompatChangeEnabled(FORCE_RESIZE_APP));
+ return getAppCompatOverrides().shouldOverrideForceResizeApp();
}
/**
@@ -510,8 +230,7 @@
* </ul>
*/
boolean shouldOverrideForceNonResizeApp() {
- return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
- isCompatChangeEnabled(FORCE_NON_RESIZE_APP));
+ return getAppCompatOverrides().shouldOverrideForceNonResizeApp();
}
/**
@@ -519,7 +238,8 @@
* android.app.Activity#setRequestedOrientation}.
*/
void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) {
- mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching;
+ getAppCompatOverrides().getAppCompatOrientationOverrides()
+ .setRelaunchingAfterRequestedOrientationChanged(isRelaunching);
}
/**
@@ -534,7 +254,7 @@
}
boolean isOverrideRespectRequestedOrientationEnabled() {
- return mIsOverrideRespectRequestedOrientationEnabled;
+ return getAppCompatOverrides().isOverrideRespectRequestedOrientationEnabled();
}
/**
@@ -551,101 +271,11 @@
* </ul>
*/
boolean shouldUseDisplayLandscapeNaturalOrientation() {
- return mAllowDisplayOrientationOverrideOptProp
- .shouldEnableWithOptInOverrideAndOptOutProperty(
- isCompatChangeEnabled(OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION));
- }
-
- @ScreenOrientation
- int overrideOrientationIfNeeded(@ScreenOrientation int candidate) {
- final DisplayContent displayContent = mActivityRecord.mDisplayContent;
- final boolean isIgnoreOrientationRequestEnabled = displayContent != null
- && displayContent.getIgnoreOrientationRequest();
- if (shouldApplyUserFullscreenOverride() && isIgnoreOrientationRequestEnabled
- // Do not override orientation to fullscreen for camera activities.
- // Fixed-orientation activities are rarely tested in other orientations, and it
- // often results in sideways or stretched previews. As the camera compat treatment
- // targets fixed-orientation activities, overriding the orientation disables the
- // treatment.
- && !mActivityRecord.isCameraActive()) {
- Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
- + mActivityRecord + " is overridden to "
- + screenOrientationToString(SCREEN_ORIENTATION_USER)
- + " by user aspect ratio settings.");
- return SCREEN_ORIENTATION_USER;
- }
-
- // In some cases (e.g. Kids app) we need to map the candidate orientation to some other
- // orientation.
- candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
-
- if (shouldApplyUserMinAspectRatioOverride() && (!isFixedOrientation(candidate)
- || candidate == SCREEN_ORIENTATION_LOCKED)) {
- Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
- + mActivityRecord + " is overridden to "
- + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT)
- + " by user aspect ratio settings.");
- return SCREEN_ORIENTATION_PORTRAIT;
- }
-
- if (mAllowOrientationOverrideOptProp.isFalse()) {
- return candidate;
- }
-
- if (mIsOverrideOrientationOnlyForCameraEnabled && displayContent != null
- && (displayContent.mDisplayRotationCompatPolicy == null
- || !displayContent.mDisplayRotationCompatPolicy
- .isActivityEligibleForOrientationOverride(mActivityRecord))) {
- return candidate;
- }
-
- // mUserAspectRatio is always initialized first in shouldApplyUserFullscreenOverride(),
- // which will always come first before this check as user override > device
- // manufacturer override.
- if (isSystemOverrideToFullscreenEnabled() && isIgnoreOrientationRequestEnabled
- // Do not override orientation to fullscreen for camera activities.
- // Fixed-orientation activities are rarely tested in other orientations, and it
- // often results in sideways or stretched previews. As the camera compat treatment
- // targets fixed-orientation activities, overriding the orientation disables the
- // treatment.
- && !mActivityRecord.isCameraActive()) {
- Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
- + mActivityRecord + " is overridden to "
- + screenOrientationToString(SCREEN_ORIENTATION_USER));
- return SCREEN_ORIENTATION_USER;
- }
-
- if (mIsOverrideToReverseLandscapeOrientationEnabled
- && (isFixedOrientationLandscape(candidate) || mIsOverrideAnyOrientationEnabled)) {
- Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
- + mActivityRecord + " is overridden to "
- + screenOrientationToString(SCREEN_ORIENTATION_REVERSE_LANDSCAPE));
- return SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
- }
-
- if (!mIsOverrideAnyOrientationEnabled && isFixedOrientation(candidate)) {
- return candidate;
- }
-
- if (mIsOverrideToPortraitOrientationEnabled) {
- Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
- + mActivityRecord + " is overridden to "
- + screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT));
- return SCREEN_ORIENTATION_PORTRAIT;
- }
-
- if (mIsOverrideToNosensorOrientationEnabled) {
- Slog.w(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
- + mActivityRecord + " is overridden to "
- + screenOrientationToString(SCREEN_ORIENTATION_NOSENSOR));
- return SCREEN_ORIENTATION_NOSENSOR;
- }
-
- return candidate;
+ return getAppCompatOverrides().shouldUseDisplayLandscapeNaturalOrientation();
}
boolean isOverrideOrientationOnlyForCameraEnabled() {
- return mIsOverrideOrientationOnlyForCameraEnabled;
+ return getAppCompatOverrides().isOverrideOrientationOnlyForCameraEnabled();
}
/**
@@ -660,8 +290,7 @@
* </ul>
*/
boolean shouldRefreshActivityForCameraCompat() {
- return mCameraCompatAllowRefreshOptProp.shouldEnableWithOptOutOverrideAndProperty(
- isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH));
+ return getAppCompatOverrides().shouldRefreshActivityForCameraCompat();
}
/**
@@ -679,8 +308,7 @@
* </ul>
*/
boolean shouldRefreshActivityViaPauseForCameraCompat() {
- return mCameraCompatEnableRefreshViaPauseOptProp.shouldEnableWithOverrideAndProperty(
- isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE));
+ return getAppCompatOverrides().shouldRefreshActivityViaPauseForCameraCompat();
}
/**
@@ -695,8 +323,7 @@
* </ul>
*/
boolean shouldForceRotateForCameraCompat() {
- return mCameraCompatAllowForceRotationOptProp.shouldEnableWithOptOutOverrideAndProperty(
- isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION));
+ return getAppCompatOverrides().shouldForceRotateForCameraCompat();
}
/**
@@ -714,18 +341,7 @@
* </ul>
*/
boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return Flags.cameraCompatForFreeform() && !isCompatChangeEnabled(
- OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
- }
-
- private boolean isCameraCompatTreatmentActive() {
- DisplayContent displayContent = mActivityRecord.mDisplayContent;
- if (displayContent == null) {
- return false;
- }
- return displayContent.mDisplayRotationCompatPolicy != null
- && displayContent.mDisplayRotationCompatPolicy
- .isTreatmentEnabledForActivity(mActivityRecord);
+ return getAppCompatOverrides().shouldApplyFreeformTreatmentForCameraCompat();
}
@FreeformCameraCompatMode
@@ -785,16 +401,18 @@
}
void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
- updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction());
+ updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction(),
+ mActivityRecord.getPendingTransaction());
}
- void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint, @NonNull Transaction t,
+ @NonNull Transaction inputT) {
if (shouldNotLayoutLetterbox(winHint)) {
return;
}
layoutLetterboxIfNeeded(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(t);
+ mLetterbox.applySurfaceChanges(t, inputT);
}
}
@@ -850,7 +468,8 @@
// For this reason we use ActivityRecord#getBounds() that the translucent activity
// inherits from the first opaque activity beneath and also takes care of the scaling
// in case of activities in size compat mode.
- final Rect innerFrame = mActivityRecord.mTransparentPolicy.isRunning()
+ final Rect innerFrame = mActivityRecord.mAppCompatController
+ .getTransparentPolicy().isRunning()
? mActivityRecord.getBounds() : w.getFrame();
mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
if (mDoubleTapEvent) {
@@ -987,7 +606,7 @@
// Don't resize to split screen size when in book mode if letterbox position is centered
return (isBookMode && isNotCenteredHorizontally || isTabletopMode && isLandscape)
|| isCameraCompatSplitScreenAspectRatioAllowed()
- && isCameraCompatTreatmentActive();
+ && getAppCompatOverrides().isCameraCompatTreatmentActive();
}
private float getDefaultMinAspectRatioForUnresizableApps() {
@@ -1089,13 +708,7 @@
* Whether we should enable users to resize the current app.
*/
boolean shouldEnableUserAspectRatioSettings() {
- // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
- // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
- // the current app doesn't opt-out so the first part of the predicate is true.
- return !mAllowUserAspectRatioOverrideOptProp.isFalse()
- && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
- && mActivityRecord.mDisplayContent != null
- && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
+ return getAppCompatOverrides().shouldEnableUserAspectRatioSettings();
}
/**
@@ -1114,15 +727,6 @@
&& mUserAspectRatio != USER_MIN_ASPECT_RATIO_FULLSCREEN;
}
- boolean isUserFullscreenOverrideEnabled() {
- if (mAllowUserAspectRatioOverrideOptProp.isFalse()
- || mAllowUserAspectRatioFullscreenOverrideOptProp.isFalse()
- || !mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled()) {
- return false;
- }
- return true;
- }
-
boolean shouldApplyUserFullscreenOverride() {
if (isUserFullscreenOverrideEnabled()) {
mUserAspectRatio = getUserMinAspectRatioOverrideCode();
@@ -1133,11 +737,12 @@
return false;
}
+ boolean isUserFullscreenOverrideEnabled() {
+ return getAppCompatOverrides().isUserFullscreenOverrideEnabled();
+ }
+
boolean isSystemOverrideToFullscreenEnabled() {
- return mIsSystemOverrideToFullscreenEnabled
- && !mAllowOrientationOverrideOptProp.isFalse()
- && (mUserAspectRatio == USER_MIN_ASPECT_RATIO_UNSET
- || mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ return getAppCompatOverrides().isSystemOverrideToFullscreenEnabled(mUserAspectRatio);
}
boolean hasFullscreenOverride() {
@@ -1311,8 +916,9 @@
? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty
- final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy
- .getFirstOpaqueActivity().map(ActivityRecord::getScreenResolvedBounds)
+ final Rect opaqueActivityBounds = mActivityRecord.mAppCompatController
+ .getTransparentPolicy().getFirstOpaqueActivity()
+ .map(ActivityRecord::getScreenResolvedBounds)
.orElse(mActivityRecord.getScreenResolvedBounds());
return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
@@ -1331,6 +937,11 @@
return isHorizontalReachabilityEnabled() || isVerticalReachabilityEnabled();
}
+ // TODO(b/346264992): Remove after AppCompatController refactoring
+ private AppCompatOverrides getAppCompatOverrides() {
+ return mActivityRecord.mAppCompatController.getAppCompatOverrides();
+ }
+
/**
* Whether vertical reachability is enabled for an activity in the current configuration.
*
@@ -1350,8 +961,9 @@
? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty.
- final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy
- .getFirstOpaqueActivity().map(ActivityRecord::getScreenResolvedBounds)
+ final Rect opaqueActivityBounds = mActivityRecord.mAppCompatController
+ .getTransparentPolicy().getFirstOpaqueActivity()
+ .map(ActivityRecord::getScreenResolvedBounds)
.orElse(mActivityRecord.getScreenResolvedBounds());
return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
@@ -1368,7 +980,8 @@
@VisibleForTesting
boolean shouldShowLetterboxUi(WindowState mainWindow) {
- if (mIsRelaunchingAfterRequestedOrientationChanged) {
+ if (getAppCompatOverrides().getAppCompatOrientationOverrides()
+ .getIsRelaunchingAfterRequestedOrientationChanged()) {
return mLastShouldShowLetterboxUi;
}
@@ -1457,7 +1070,7 @@
// corners because we assume the specific layout would. This is the case when the layout
// of the translucent activity uses only a part of all the bounds because of the use of
// LayoutParams.WRAP_CONTENT.
- if (mActivityRecord.mTransparentPolicy.isRunning()
+ if (mActivityRecord.mAppCompatController.getTransparentPolicy().isRunning()
&& (cropBounds.width() != mainWindow.mRequestedWidth
|| cropBounds.height() != mainWindow.mRequestedHeight)) {
return null;
@@ -1528,7 +1141,8 @@
}
boolean getIsRelaunchingAfterRequestedOrientationChanged() {
- return mIsRelaunchingAfterRequestedOrientationChanged;
+ return getAppCompatOverrides().getAppCompatOrientationOverrides()
+ .getIsRelaunchingAfterRequestedOrientationChanged();
}
private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) {
@@ -1713,13 +1327,13 @@
int getLetterboxPositionForLogging() {
int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
if (isHorizontalReachabilityEnabled()) {
- int letterboxPositionForHorizontalReachability = getLetterboxConfiguration()
+ int letterboxPositionForHorizontalReachability = mLetterboxConfiguration
.getLetterboxPositionForHorizontalReachability(
isDisplayFullScreenAndInPosture(/* isTabletop */ false));
positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
letterboxPositionForHorizontalReachability);
} else if (isVerticalReachabilityEnabled()) {
- int letterboxPositionForVerticalReachability = getLetterboxConfiguration()
+ int letterboxPositionForVerticalReachability = mLetterboxConfiguration
.getLetterboxPositionForVerticalReachability(
isDisplayFullScreenAndInPosture(/* isTabletop */ true));
positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
@@ -1728,10 +1342,6 @@
return positionToLog;
}
- private LetterboxConfiguration getLetterboxConfiguration() {
- return mLetterboxConfiguration;
- }
-
/**
* Logs letterbox position changes via {@link ActivityMetricsLogger#logLetterboxPositionChange}.
*/
@@ -1761,21 +1371,4 @@
w.mAttrs.insetsFlags.appearance
);
}
-
- @NonNull
- private static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) {
- return new BooleanSupplier() {
- private boolean mRead;
- private boolean mValue;
-
- @Override
- public boolean getAsBoolean() {
- if (!mRead) {
- mRead = true;
- mValue = supplier.getAsBoolean();
- }
- return mValue;
- }
- };
- }
}
diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
index 4d3b95b..c22b07a 100644
--- a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
+++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
@@ -104,10 +104,9 @@
final IDisplayChangeWindowCallback remoteCallback = createCallback(callback);
try {
- mService.mH.removeCallbacks(mTimeoutRunnable);
- mService.mH.postDelayed(mTimeoutRunnable, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS);
mService.mDisplayChangeController.onDisplayChange(mDisplayContent.mDisplayId,
fromRotation, toRotation, newDisplayAreaInfo, remoteCallback);
+ mService.mH.postDelayed(mTimeoutRunnable, callback, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS);
return true;
} catch (RemoteException e) {
Slog.e(TAG, "Exception while dispatching remote display-change", e);
@@ -118,6 +117,7 @@
private void onContinueTimedOut() {
Slog.e(TAG, "RemoteDisplayChange timed-out, UI might get messed-up after this.");
+ mService.mH.removeCallbacks(mTimeoutRunnable);
// timed-out, so run all continue callbacks and clear the list
synchronized (mService.mGlobalLock) {
for (int i = 0; i < mCallbacks.size(); ++i) {
@@ -172,9 +172,6 @@
// The "toIndex" is exclusive, so it needs +1 to clear the current calling callback.
mCallbacks.subList(0, idx + 1).clear();
final boolean completed = mCallbacks.isEmpty();
- if (completed) {
- mService.mH.removeCallbacks(mTimeoutRunnable);
- }
callback.onContinueRemoteDisplayChange(transaction);
if (completed) {
onCompleted();
@@ -198,6 +195,7 @@
}
mService.mH.post(() -> RemoteDisplayChangeController.this
.continueDisplayChange(callback, t));
+ mService.mH.removeCallbacks(mTimeoutRunnable, callback);
}
}
};
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index f5ab38f..6abd488 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -152,7 +152,6 @@
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.Slogf;
-import com.android.window.flags.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -789,13 +788,12 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- if (Flags.bundleClientTransactionFlag()) {
- // mWmService.mResizingWindows is populated in #applySurfaceChangesTransaction()
- handleResizingWindows();
+ // mWmService.mResizingWindows is populated in #applySurfaceChangesTransaction()
+ handleResizingWindows();
+ clearFrameChangingWindows();
- // Called after #handleResizingWindows to include WindowStateResizeItem if any.
- mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
- }
+ // Called after #handleResizingWindows to include WindowStateResizeItem if any.
+ mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -839,11 +837,6 @@
}
}
- if (!Flags.bundleClientTransactionFlag()) {
- handleResizingWindows();
- }
- clearFrameChangingWindows();
-
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
@@ -1905,6 +1898,7 @@
// Don't do recursive work.
return;
}
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RWC_ensureActivitiesVisible");
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
// First the front root tasks. In case any are not fullscreen and are in front of home.
@@ -1914,6 +1908,7 @@
}
} finally {
mTaskSupervisor.endActivityVisibilityUpdate();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3b3eeb4..4a0239b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -41,7 +41,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -64,7 +63,6 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.MergedConfiguration;
import android.util.Slog;
import android.view.IWindow;
import android.view.IWindowId;
@@ -81,7 +79,6 @@
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
import android.view.WindowRelayoutResult;
-import android.window.ClientWindowFrames;
import android.window.InputTransferToken;
import android.window.OnBackInvokedCallbackInfo;
@@ -290,37 +287,12 @@
return res;
}
- /** @deprecated */
- @Deprecated
- @Override
- public int relayoutLegacy(IWindow window, WindowManager.LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
- int lastSyncSeqId, ClientWindowFrames outFrames,
- MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl,
- InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
- Bundle outBundle) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
- int res = mService.relayoutWindow(this, window, attrs,
- requestedWidth, requestedHeight, viewFlags, flags, seq,
- lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
- outActiveControls, outBundle);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- return res;
- }
-
@Override
public void relayoutAsync(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
int lastSyncSeqId) {
- if (windowSessionRelayoutInfo()) {
- relayout(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
- lastSyncSeqId, null /* outRelayoutResult */);
- } else {
- relayoutLegacy(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
- lastSyncSeqId, null /* outFrames */, null /* mergedConfiguration */,
- null /* outSurfaceControl */, null /* outInsetsState */,
- null /* outActiveControls */, null /* outSyncIdBundle */);
- }
+ relayout(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
+ lastSyncSeqId, null /* outRelayoutResult */);
}
@Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 787c5d6..f9c53aa 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -47,8 +47,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -1994,6 +1992,12 @@
final boolean wasInMultiWindowMode = inMultiWindowMode();
final boolean wasInPictureInPicture = inPinnedWindowingMode();
super.onConfigurationChanged(newParentConfig);
+ if (mDisplayContent == null) {
+ // This should be initializing from Task.Builder. The onConfigurationChanged will be
+ // called again when this task is attached to hierarchy. Early return here because the
+ // following operations are no-op for a non-attached task.
+ return;
+ }
// Only need to update surface size here since the super method will handle updating
// surface position.
updateSurfaceSize(getSyncTransaction());
@@ -3580,15 +3584,29 @@
? null : new PictureInPictureParams(top.pictureInPictureArgs);
}
- Rect getDisplayCutoutInsets() {
- if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null;
+ /** @return The display cutout insets where the main window is not allowed to extend to. */
+ @NonNull Rect getDisplayCutoutInsets() {
+ final Rect displayCutoutInsets = new Rect();
+ if (mDisplayContent == null || getDisplayInfo().displayCutout == null) {
+ return displayCutoutInsets;
+ }
final WindowState w = getTopVisibleAppMainWindow();
- final int displayCutoutMode = w == null
- ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
- : w.getAttrs().layoutInDisplayCutoutMode;
- return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
- ? null : getDisplayInfo().displayCutout.getSafeInsets();
+ final Rect displayFrame;
+ if (w != null && w.mHaveFrame) {
+ displayFrame = w.getDisplayFrame();
+ } else {
+ displayFrame = mDisplayContent.getBounds();
+ displayFrame.inset(getDisplayInfo().displayCutout.getSafeInsets());
+ }
+ final Rect taskBounds = getBounds();
+ if (displayCutoutInsets.setIntersect(taskBounds, displayFrame)) {
+ displayCutoutInsets.set(
+ displayCutoutInsets.left - taskBounds.left,
+ displayCutoutInsets.top - taskBounds.top,
+ taskBounds.right - displayCutoutInsets.right,
+ taskBounds.bottom - displayCutoutInsets.bottom);
+ }
+ return displayCutoutInsets;
}
/**
@@ -4786,8 +4804,7 @@
// task is still updated by core. Otherwise if the task is collected (e.g.
// rotation change) after leaving this scope, the visibility operation will be
// put in sync transaction, then it is not synced with reparent.
- if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()
- && lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
+ if (lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
lastParentBeforePip.prepareSurfaces();
// If the moveToFront is a part of finishing transition, then make sure
// the z-order of tasks are up-to-date.
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index ab72e3c..3cd071b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -29,6 +29,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.isFloating;
import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -81,7 +82,6 @@
import android.app.ResultInfo;
import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.ResumeActivityItem;
@@ -1603,9 +1603,6 @@
try {
final IApplicationThread appThread = next.app.getThread();
- final ClientTransaction transaction = Flags.bundleClientTransactionFlag()
- ? null
- : ClientTransaction.obtain(appThread);
// Deliver all pending results.
final ArrayList<ResultInfo> a = next.results;
if (a != null) {
@@ -1616,24 +1613,16 @@
}
final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
next.token, a);
- if (transaction == null) {
- mAtmService.getLifecycleManager().scheduleTransactionItem(
- appThread, activityResultItem);
- } else {
- transaction.addTransactionItem(activityResultItem);
- }
+ mAtmService.getLifecycleManager().scheduleTransactionItem(
+ appThread, activityResultItem);
}
}
if (next.newIntents != null) {
final NewIntentItem newIntentItem = NewIntentItem.obtain(
next.token, next.newIntents, true /* resume */);
- if (transaction == null) {
- mAtmService.getLifecycleManager().scheduleTransactionItem(
- appThread, newIntentItem);
- } else {
- transaction.addTransactionItem(newIntentItem);
- }
+ mAtmService.getLifecycleManager().scheduleTransactionItem(
+ appThread, newIntentItem);
}
// Well the app will no longer be stopped.
@@ -1650,13 +1639,8 @@
final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
next.token, topProcessState, dc.isNextTransitionForward(),
next.shouldSendCompatFakeFocus());
- if (transaction == null) {
- mAtmService.getLifecycleManager().scheduleTransactionItem(
- appThread, resumeActivityItem);
- } else {
- transaction.addTransactionItem(resumeActivityItem);
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
- }
+ mAtmService.getLifecycleManager().scheduleTransactionItem(
+ appThread, resumeActivityItem);
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
} catch (Exception e) {
@@ -2241,15 +2225,17 @@
static class ConfigOverrideHint {
@Nullable DisplayInfo mTmpOverrideDisplayInfo;
@Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
- @Nullable Rect mTmpParentAppBoundsOverride;
+ @Nullable Rect mParentAppBoundsOverride;
int mTmpOverrideConfigOrientation;
boolean mUseOverrideInsetsForConfig;
void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig,
boolean isFixedRotationTransforming) {
- mTmpParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
+ mParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
+ mTmpOverrideConfigOrientation = parentConfig.orientation;
final Insets insets;
- if (mUseOverrideInsetsForConfig && dc != null) {
+ if (mUseOverrideInsetsForConfig && dc != null
+ && !isFloating(parentConfig.windowConfiguration.getWindowingMode())) {
// Insets are decoupled from configuration by default from V+, use legacy
// compatibility behaviour for apps targeting SDK earlier than 35
// (see applySizeOverrideIfNeeded).
@@ -2269,13 +2255,12 @@
} else {
insets = Insets.NONE;
}
- mTmpParentAppBoundsOverride.inset(insets);
+ mParentAppBoundsOverride.inset(insets);
}
void resetTmpOverrides() {
mTmpOverrideDisplayInfo = null;
mTmpCompatInsets = null;
- mTmpParentAppBoundsOverride = null;
mTmpOverrideConfigOrientation = ORIENTATION_UNDEFINED;
}
}
@@ -2364,7 +2349,7 @@
final Rect containingAppBounds;
if (insideParentBounds) {
containingAppBounds = useOverrideInsetsForConfig
- ? overrideHint.mTmpParentAppBoundsOverride
+ ? overrideHint.mParentAppBoundsOverride
: parentConfig.windowConfiguration.getAppBounds();
} else {
// Restrict appBounds to display non-decor rather than parent because the
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index c4e932a..26315f9 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -171,11 +171,7 @@
TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
boolean isSystemOrganizer) {
- if (Flags.bundleClientTransactionFlag()) {
- mAppThread = getAppThread(pid, uid);
- } else {
- mAppThread = null;
- }
+ mAppThread = getAppThread(pid, uid);
mOrganizer = organizer;
mOrganizerPid = pid;
mOrganizerUid = uid;
@@ -431,13 +427,9 @@
return;
}
try {
- if (Flags.bundleClientTransactionFlag()) {
- // Dispatch through IApplicationThread to ensure the binder call is in order
- // with ClientTransaction.
- mAppThread.scheduleTaskFragmentTransaction(mOrganizer, transaction);
- } else {
- mOrganizer.onTransactionReady(transaction);
- }
+ // Dispatch through IApplicationThread to ensure the binder call is in order
+ // with ClientTransaction.
+ mAppThread.scheduleTaskFragmentTransaction(mOrganizer, transaction);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
return;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index bc45c70..e698a3d 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -68,6 +68,8 @@
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
import android.annotation.IntDef;
@@ -1195,7 +1197,6 @@
throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId);
}
mController.mFinishingTransition = this;
-
if (mTransientHideTasks != null && !mTransientHideTasks.isEmpty()) {
// The transient hide tasks could be occluded now, e.g. returning to home. So trigger
// the update to make the activities in the tasks invisible-requested, then the next
@@ -1380,7 +1381,10 @@
// If the activity was just inserted to an invisible task, it will keep INITIALIZING
// state. Then no need to notify the callback to avoid clearing some states
// unexpectedly, e.g. launch-task-behind.
- if (ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING)) {
+ // However, skip dispatch to predictive back animation target, because it only set
+ // launch-task-behind to make the activity become visible.
+ if ((ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING))
+ && !ar.isAnimating(PARENTS, ANIMATION_TYPE_PREDICT_BACK)) {
mController.dispatchLegacyAppTransitionFinished(ar);
}
@@ -1903,7 +1907,10 @@
} else {
final List<TransitionInfo.Change> changes = info.getChanges();
for (int i = changes.size() - 1; i >= 0; --i) {
- if (mTargets.get(i).mContainer.asActivityRecord() != null) {
+ final WindowContainer<?> container = mTargets.get(i).mContainer;
+ if (container.asActivityRecord() != null
+ || (container.asTask() != null
+ && mOverrideOptions.getOverrideTaskTransition())) {
changes.get(i).setAnimationOptions(mOverrideOptions);
// TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
@@ -2541,9 +2548,9 @@
if (wc.asWindowState() != null) continue;
final ChangeInfo changeInfo = changes.get(wc);
-
- // Reject no-ops
- if (!changeInfo.hasChanged()) {
+ // Reject no-ops, unless wallpaper
+ if (!changeInfo.hasChanged()
+ && (!Flags.ensureWallpaperInTransitions() || wc.asWallpaperToken() == null)) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" Rejecting as no-op: %s", wc);
continue;
@@ -3228,6 +3235,12 @@
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_WINDOW_MANAGER, TAG, cookie);
}
+ boolean hasChanged(WindowContainer wc) {
+ final ChangeInfo chg = mChanges.get(wc);
+ if (chg == null) return false;
+ return chg.hasChanged();
+ }
+
@VisibleForTesting
static class ChangeInfo {
private static final int FLAG_NONE = 0;
@@ -3510,10 +3523,15 @@
Slog.e(TAG, "#" + mSyncId + " readiness timeout, used=" + mReadyTrackerOld.mUsed
+ " deferReadyDepth=" + mReadyTrackerOld.mDeferReadyDepth
+ " group=" + mReadyTrackerOld.mReadyGroups);
- return;
+ } else {
+ Slog.e(TAG, "#" + mSyncId + " met conditions: " + mReadyTracker.mMet);
+ Slog.e(TAG, "#" + mSyncId + " unmet conditions: " + mReadyTracker.mConditions);
}
- Slog.e(TAG, "#" + mSyncId + " met conditions: " + mReadyTracker.mMet);
- Slog.e(TAG, "#" + mSyncId + " unmet conditions: " + mReadyTracker.mConditions);
+ // Make sure the pending display change can be applied (especially DC#mWaitingForConfig)
+ // in case shell hasn't called WindowOrganizerController#startTransition yet.
+ if (mState < STATE_STARTED && this == mController.getCollectingTransition()) {
+ applyDisplayChangeIfNeeded(new ArraySet<>());
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
index b408397..3044abd 100644
--- a/services/core/java/com/android/server/wm/TransparentPolicy.java
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -260,8 +260,9 @@
private void start(@NonNull ActivityRecord firstOpaqueActivity) {
mFirstOpaqueActivity = firstOpaqueActivity;
- mFirstOpaqueActivity.mTransparentPolicy
- .mDestroyListeners.add(mActivityRecord.mTransparentPolicy);
+ mFirstOpaqueActivity.mAppCompatController.getTransparentPolicy()
+ .mDestroyListeners.add(mActivityRecord.mAppCompatController
+ .getTransparentPolicy());
inheritFromOpaque(firstOpaqueActivity);
final WindowContainer<?> parent = mActivityRecord.getParent();
mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
@@ -312,8 +313,9 @@
mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
mInheritedCompatDisplayInsets = null;
if (mFirstOpaqueActivity != null) {
- mFirstOpaqueActivity.mTransparentPolicy
- .mDestroyListeners.remove(mActivityRecord.mTransparentPolicy);
+ mFirstOpaqueActivity.mAppCompatController.getTransparentPolicy()
+ .mDestroyListeners.remove(mActivityRecord.mAppCompatController
+ .getTransparentPolicy());
}
mFirstOpaqueActivity = null;
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 3e43f5a..43f7ecc 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -60,6 +60,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -334,13 +335,13 @@
}
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
- token.setVisibility(false);
if (token.isVisible()) {
ProtoLog.d(WM_DEBUG_WALLPAPER,
"Hiding wallpaper %s from %s target=%s prev=%s callers=%s",
token, winGoingAway, mWallpaperTarget, mPrevWallpaperTarget,
Debug.getCallers(5));
}
+ token.setVisibility(false);
}
}
@@ -764,10 +765,19 @@
void collectTopWallpapers(Transition transition) {
if (mFindResults.hasTopShowWhenLockedWallpaper()) {
- transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
+ if (Flags.ensureWallpaperInTransitions()) {
+ transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper.mToken);
+ } else {
+ transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
+ }
+
}
if (mFindResults.hasTopHideWhenLockedWallpaper()) {
- transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
+ if (Flags.ensureWallpaperInTransitions()) {
+ transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper.mToken);
+ } else {
+ transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 9d1551c..b7f8505 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -32,6 +32,7 @@
import android.util.SparseArray;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
import java.util.function.Consumer;
@@ -80,6 +81,20 @@
mDisplayContent.mWallpaperController.removeWallpaperToken(this);
}
+ @Override
+ public void prepareSurfaces() {
+ super.prepareSurfaces();
+
+ if (Flags.ensureWallpaperInTransitions()) {
+ // Similar to Task.prepareSurfaces, outside of transitions we need to apply visibility
+ // changes directly. In transitions the transition player will take care of applying the
+ // visibility change.
+ if (!mTransitionController.inTransition(this)) {
+ getSyncTransaction().setVisibility(mSurfaceControl, isVisible());
+ }
+ }
+ }
+
/**
* Controls whether this wallpaper shows underneath the keyguard or is hidden and only
* revealed once keyguard is dismissed.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 70143ba..1f31af6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -202,8 +202,7 @@
private int mLastLayer = 0;
private SurfaceControl mLastRelativeToLayer = null;
- // TODO(b/132320879): Remove this from WindowContainers except DisplayContent.
- private final Transaction mPendingTransaction;
+ private Transaction mPendingTransaction;
/**
* Windows that clients are waiting to have drawn.
@@ -358,7 +357,6 @@
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mTransitionController = mWmService.mAtmService.getTransitionController();
- mPendingTransaction = wms.mTransactionFactory.get();
mSyncTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
mSurfaceFreezer = new SurfaceFreezer(this, wms);
@@ -580,6 +578,10 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
+ if (mParent == null) {
+ // Avoid unnecessary surface operation before attaching to a parent.
+ return;
+ }
updateSurfacePositionNonOrganized();
scheduleAnimation();
if (mOverlayHost != null) {
@@ -1074,8 +1076,9 @@
}
}
mDisplayContent = dc;
- if (dc != null && dc != this) {
+ if (dc != null && dc != this && mPendingTransaction != null) {
dc.getPendingTransaction().merge(mPendingTransaction);
+ mPendingTransaction = null;
}
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mChildren.get(i);
@@ -2922,14 +2925,17 @@
@Override
public Transaction getPendingTransaction() {
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent != null && displayContent != this) {
- return displayContent.getPendingTransaction();
+ final WindowContainer<?> dc = mDisplayContent;
+ if (dc != null && dc.mPendingTransaction != null) {
+ return dc.mPendingTransaction;
}
// This WindowContainer has not attached to a display yet or this is a DisplayContent, so we
// let the caller to save the surface operations within the local mPendingTransaction.
// If this is not a DisplayContent, we will merge it to the pending transaction of its
// display once it attaches to it.
+ if (mPendingTransaction == null) {
+ mPendingTransaction = mWmService.mTransactionFactory.get();
+ }
return mPendingTransaction;
}
@@ -3980,6 +3986,19 @@
}
/**
+ * Returns {@code true} if this window container belongs to a different sync group than the
+ * given group.
+ */
+ boolean isDifferentSyncGroup(@Nullable BLASTSyncEngine.SyncGroup group) {
+ if (group == null) return false;
+ final BLASTSyncEngine.SyncGroup thisGroup = getSyncGroup();
+ if (thisGroup == null || group == thisGroup) return false;
+ Slog.d(TAG, this + " uses a different SyncGroup, current=" + thisGroup.mSyncId
+ + " given=" + group.mSyncId);
+ return true;
+ }
+
+ /**
* Recursively finishes/cleans-up sync state of this subtree and collects all the sync
* transactions into `outMergedTransaction`.
* @param outMergedTransaction A transaction to merge all the recorded sync operations into.
@@ -3988,10 +4007,14 @@
*/
void finishSync(Transaction outMergedTransaction, @Nullable BLASTSyncEngine.SyncGroup group,
boolean cancel) {
- if (mSyncState == SYNC_STATE_NONE) return;
- final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
- // If it's null, then we need to clean-up anyways.
- if (syncGroup != null && group != syncGroup) return;
+ if (mSyncState == SYNC_STATE_NONE) {
+ if (mSyncGroup != null) {
+ Slog.e(TAG, "finishSync: stale group " + mSyncGroup.mSyncId + " of " + this);
+ mSyncGroup = null;
+ }
+ return;
+ }
+ if (isDifferentSyncGroup(group)) return;
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
outMergedTransaction.merge(mSyncTransaction);
for (int i = mChildren.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2b375e1..5215609 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2292,32 +2292,7 @@
outInsetsState = null;
outActiveControls = null;
}
- return relayoutWindowInner(session, client, attrs, requestedWidth, requestedHeight,
- viewVisibility, flags, seq, lastSyncSeqId, outFrames, outMergedConfiguration,
- outSurfaceControl, outInsetsState, outActiveControls, null /* outBundle */,
- outRelayoutResult);
- }
- /** @deprecated */
- @Deprecated
- public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
- int lastSyncSeqId, ClientWindowFrames outFrames,
- MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
- InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
- Bundle outBundle) {
- return relayoutWindowInner(session, client, attrs, requestedWidth, requestedHeight,
- viewVisibility, flags, seq, lastSyncSeqId, outFrames, outMergedConfiguration,
- outSurfaceControl, outInsetsState, outActiveControls, outBundle,
- null /* outRelayoutResult */);
- }
-
- private int relayoutWindowInner(Session session, IWindow client, LayoutParams attrs,
- int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
- int lastSyncSeqId, ClientWindowFrames outFrames,
- MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
- InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
- Bundle outBundle, WindowRelayoutResult outRelayoutResult) {
if (outActiveControls != null) {
outActiveControls.set(null, false /* copyControls */);
}
@@ -2649,14 +2624,8 @@
}
if (outFrames != null && outMergedConfiguration != null) {
- final boolean shouldReportActivityWindowInfo;
- if (Flags.windowSessionRelayoutInfo()) {
- shouldReportActivityWindowInfo = outRelayoutResult != null
+ final boolean shouldReportActivityWindowInfo = outRelayoutResult != null
&& win.mLastReportedActivityWindowInfo != null;
- } else {
- shouldReportActivityWindowInfo = outBundle != null
- && win.mLastReportedActivityWindowInfo != null;
- }
final ActivityWindowInfo outActivityWindowInfo = shouldReportActivityWindowInfo
? new ActivityWindowInfo()
: null;
@@ -2665,13 +2634,7 @@
outActivityWindowInfo, false /* useLatestConfig */, shouldRelayout);
if (shouldReportActivityWindowInfo) {
- if (Flags.windowSessionRelayoutInfo()) {
- outRelayoutResult.activityWindowInfo = outActivityWindowInfo;
- } else {
- outBundle.putParcelable(
- IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO,
- outActivityWindowInfo);
- }
+ outRelayoutResult.activityWindowInfo = outActivityWindowInfo;
}
// Set resize-handled here because the values are sent back to the client.
@@ -2702,28 +2665,16 @@
win.isVisible() /* visible */, false /* removed */);
}
- if (Flags.windowSessionRelayoutInfo()) {
- if (outRelayoutResult != null) {
- if (win.syncNextBuffer() && viewVisibility == View.VISIBLE
- && win.mSyncSeqId > lastSyncSeqId) {
- outRelayoutResult.syncSeqId = win.shouldSyncWithBuffers()
- ? win.mSyncSeqId
- : -1;
- win.markRedrawForSyncReported();
- } else {
- outRelayoutResult.syncSeqId = -1;
- }
- }
- } else if (outBundle != null) {
- final int maybeSyncSeqId;
+ if (outRelayoutResult != null) {
if (win.syncNextBuffer() && viewVisibility == View.VISIBLE
&& win.mSyncSeqId > lastSyncSeqId) {
- maybeSyncSeqId = win.shouldSyncWithBuffers() ? win.mSyncSeqId : -1;
+ outRelayoutResult.syncSeqId = win.shouldSyncWithBuffers()
+ ? win.mSyncSeqId
+ : -1;
win.markRedrawForSyncReported();
} else {
- maybeSyncSeqId = -1;
+ outRelayoutResult.syncSeqId = -1;
}
- outBundle.putInt(IWindowSession.KEY_RELAYOUT_BUNDLE_SEQID, maybeSyncSeqId);
}
if (configChanged) {
@@ -3845,7 +3796,7 @@
hideBootMessagesLocked();
// If the screen still doesn't come up after 30 seconds, give
// up and turn it on.
- mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
+ mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000 * Build.HW_TIMEOUT_MULTIPLIER);
}
mPolicy.systemBooted();
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 72109d34..d26df7a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -609,11 +609,19 @@
int effects = TRANSACT_EFFECTS_NONE;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
+ mService.mTaskSupervisor.beginDeferResume();
+ boolean deferResume = true;
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
- final boolean shouldDeferTransitionReady = transition != null && !t.isEmpty()
- && (transition.isCollecting() || Flags.alwaysDeferTransitionWhenApplyWct());
- if (shouldDeferTransitionReady) {
- transition.deferTransitionReady();
+ boolean deferTransitionReady = false;
+ if (transition != null && !t.isEmpty()) {
+ if (transition.isCollecting()) {
+ deferTransitionReady = true;
+ transition.deferTransitionReady();
+ } else if (Flags.alwaysDeferTransitionWhenApplyWct()) {
+ Slog.w(TAG, "Transition is not collecting when applyTransaction."
+ + " transition=" + transition + " state=" + transition.getState());
+ transition = null;
+ }
}
try {
final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
@@ -750,6 +758,8 @@
}
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
+ mService.mTaskSupervisor.endDeferResume();
+ deferResume = false;
// Already calls ensureActivityConfig
mService.mRootWindowContainer.ensureActivitiesVisible();
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -767,10 +777,13 @@
mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
} finally {
- if (shouldDeferTransitionReady) {
+ if (deferTransitionReady) {
transition.continueTransitionReady();
}
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
+ if (deferResume) {
+ mService.mTaskSupervisor.endDeferResume();
+ }
mService.continueWindowLayout();
}
return effects;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1c00fbb..b878072 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -309,14 +310,15 @@
private volatile boolean mWasStoppedLogged;
// The bits used for mActivityStateFlags.
- private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
- private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
- private static final int ACTIVITY_STATE_FLAG_IS_STOPPING = 1 << 18;
- private static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 1 << 19;
- private static final int ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE = 1 << 20;
- private static final int ACTIVITY_STATE_FLAG_HAS_RESUMED = 1 << 21;
- private static final int ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK = 1 << 22;
- private static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
+ public static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
+ public static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
+ public static final int ACTIVITY_STATE_FLAG_IS_STOPPING = 1 << 18;
+ public static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 1 << 19;
+ public static final int ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE = 1 << 20;
+ public static final int ACTIVITY_STATE_FLAG_HAS_RESUMED = 1 << 21;
+ public static final int ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK = 1 << 22;
+ public static final int ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN = 1 << 23;
+ public static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
/**
* The state for oom-adjustment calculation. The higher 16 bits are the activity states, and the
@@ -1211,31 +1213,13 @@
}
}
- public interface ComputeOomAdjCallback {
- void onVisibleActivity();
- void onPausedActivity();
- void onStoppingActivity(boolean finishing);
- void onOtherActivity();
- }
-
/**
- * Returns the minimum task layer rank. It should only be called if {@link #hasActivities}
- * returns {@code true}.
+ * Returns the current ACTIVITY_STATE_FLAG_* of this process. It should only be called if
+ * {@link #hasActivities} returns {@code true}.
*/
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
- public int computeOomAdjFromActivities(ComputeOomAdjCallback callback) {
- final int flags = mActivityStateFlags;
- if ((flags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0) {
- callback.onVisibleActivity();
- } else if ((flags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) {
- callback.onPausedActivity();
- } else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {
- callback.onStoppingActivity(
- (flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
- } else {
- callback.onOtherActivity();
- }
- return flags & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ public int getActivityStateFlags() {
+ return mActivityStateFlags;
}
void computeProcessActivityState() {
@@ -1256,14 +1240,25 @@
stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE;
}
final Task task = r.getTask();
- if (task != null && task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
+ if (task == null) {
+ Slog.e(TAG, "Unexpected detached " + r + " in " + this);
+ continue;
+ }
+ if (task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
}
if (r.isVisibleRequested()) {
if (r.isState(RESUMED)) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED;
+ final int windowingMode = r.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
+ && com.android.window.flags.Flags
+ .processPriorityPolicyForMultiWindowMode()
+ && task.getAdjacentTask() != null) {
+ stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN;
+ }
}
- if (task != null && minTaskLayer > 0) {
+ if (minTaskLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minTaskLayer > layer) {
minTaskLayer = layer;
@@ -2107,6 +2102,9 @@
pw.print("V|");
if ((stateFlags & ACTIVITY_STATE_FLAG_HAS_RESUMED) != 0) {
pw.print("R|");
+ if ((stateFlags & ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN) != 0) {
+ pw.print("RS|");
+ }
}
} else if ((stateFlags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) {
pw.print("P|");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dcd4bd6..d845968 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -96,6 +96,7 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+import static android.util.SequenceUtils.getNextSeq;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
@@ -3652,6 +3653,7 @@
}
}
outFrames.compatScale = getCompatScaleForClient();
+ outFrames.seq = getNextSeq(mLastReportedFrames.seq);
if (mLastReportedFrames != outFrames) {
mLastReportedFrames.setTo(outFrames);
}
@@ -3682,7 +3684,9 @@
}
void fillInsetsState(@NonNull InsetsState outInsetsState, boolean copySources) {
+ final int lastSeq = mLastReportedInsetsState.getSeq();
outInsetsState.set(getCompatInsetsState(), copySources);
+ outInsetsState.setSeq(getNextSeq(lastSeq));
if (outInsetsState != mLastReportedInsetsState) {
// No need to copy for the recorded.
mLastReportedInsetsState.set(outInsetsState, false /* copySources */);
@@ -3691,9 +3695,11 @@
void fillInsetsSourceControls(@NonNull InsetsSourceControl.Array outArray,
boolean copyControls) {
+ final int lastSeq = mLastReportedActiveControls.getSeq();
final InsetsSourceControl[] controls =
getDisplayContent().getInsetsStateController().getControlsForDispatch(this);
outArray.set(controls, copyControls);
+ outArray.setSeq(getNextSeq(lastSeq));
if (outArray != mLastReportedActiveControls) {
// No need to copy for the recorded.
mLastReportedActiveControls.setTo(outArray, false /* copyControls */);
@@ -3754,31 +3760,13 @@
final boolean isDragResizing = isDragResizing();
markRedrawForSyncReported();
-
- if (Flags.bundleClientTransactionFlag()) {
- getProcess().scheduleClientTransactionItem(
- WindowStateResizeItem.obtain(mClient, mLastReportedFrames, reportDraw,
- mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
- alwaysConsumeSystemBars, displayId,
- syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
- mLastReportedActivityWindowInfo));
- onResizePostDispatched(drawPending, prevRotation, displayId);
- } else {
- // TODO(b/301870955): cleanup after launch
- try {
- mClient.resized(mLastReportedFrames, reportDraw, mLastReportedConfiguration,
- mLastReportedInsetsState, forceRelayout, alwaysConsumeSystemBars, displayId,
+ getProcess().scheduleClientTransactionItem(
+ WindowStateResizeItem.obtain(mClient, mLastReportedFrames, reportDraw,
+ mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
+ alwaysConsumeSystemBars, displayId,
syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
- mLastReportedActivityWindowInfo);
- onResizePostDispatched(drawPending, prevRotation, displayId);
- } catch (RemoteException e) {
- // Cancel orientation change of this window to avoid blocking unfreeze display.
- setOrientationChanging(false);
- mLastFreezeDuration = (int) (SystemClock.elapsedRealtime()
- - mWmService.mDisplayFreezeTime);
- Slog.w(TAG, "Failed to report 'resized' to " + this + " due to " + e);
- }
- }
+ mLastReportedActivityWindowInfo));
+ onResizePostDispatched(drawPending, prevRotation, displayId);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -5008,12 +4996,8 @@
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final Point position = new Point();
- if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()) {
- transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
- position);
- } else {
- position.set(mSurfacePosition);
- }
+ transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
+ position);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, position, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
@@ -5791,8 +5775,7 @@
@Override
void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
boolean cancel) {
- final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
- if (syncGroup != null && group != syncGroup) return;
+ if (isDifferentSyncGroup(group)) return;
mPrepareSyncSeqId = 0;
if (cancel) {
// This is leaving sync so any buffers left in the sync have a chance of
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6fd7aa0..9ecd492 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -63,6 +63,7 @@
import com.android.internal.protolog.common.LogLevel;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
@@ -374,9 +375,13 @@
ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
mWin, new RuntimeException().fillInStackTrace());
destroySurface(t);
- // Don't hide wallpaper if we're deferring the surface destroy
- // because of a surface change.
- mWallpaperControllerLocked.hideWallpapers(mWin);
+ if (Flags.ensureWallpaperInTransitions()) {
+ if (mWallpaperControllerLocked.isWallpaperTarget(mWin)) {
+ mWin.requestUpdateWallpaperIfNeeded();
+ }
+ } else {
+ mWallpaperControllerLocked.hideWallpapers(mWin);
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
+ " surface " + mSurfaceController + " session " + mSession + ": "
@@ -431,7 +436,9 @@
if (!w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
- mWallpaperControllerLocked.hideWallpapers(w);
+ if (!w.mIsWallpaper || !Flags.ensureWallpaperInTransitions()) {
+ mWallpaperControllerLocked.hideWallpapers(w);
+ }
// If we are waiting for this window to handle an orientation change. If this window is
// really hidden (gone for layout), there is no point in still waiting for it.
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 50d48b7..5c4db24 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -45,6 +45,7 @@
TOUCHSCREEN,
DPAD,
STYLUS,
+ ROTARY_ENCODER,
};
static unique_fd invalidFd() {
@@ -114,6 +115,10 @@
ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
break;
+ case DeviceType::ROTARY_ENCODER:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ break;
default:
ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
return invalidFd();
@@ -312,6 +317,13 @@
return fd.ok() ? reinterpret_cast<jlong>(new VirtualStylus(std::move(fd))) : INVALID_PTR;
}
+static jlong nativeOpenUinputRotaryEncoder(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+ jint productId, jstring phys, jint height, jint width) {
+ auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::ROTARY_ENCODER,
+ /* screenHeight= */ 0, /* screenWidth= */ 0);
+ return fd.ok() ? reinterpret_cast<jlong>(new VirtualRotaryEncoder(std::move(fd))) : INVALID_PTR;
+}
+
static void nativeCloseUinput(JNIEnv* env, jobject thiz, jlong ptr) {
VirtualInputDevice* virtualInputDevice = reinterpret_cast<VirtualInputDevice*>(ptr);
delete virtualInputDevice;
@@ -381,6 +393,13 @@
std::chrono::nanoseconds(eventTimeNanos));
}
+static bool nativeWriteRotaryEncoderScrollEvent(JNIEnv* env, jobject thiz, jlong ptr,
+ jfloat scrollAmount, jlong eventTimeNanos) {
+ VirtualRotaryEncoder* virtualRotaryEncoder = reinterpret_cast<VirtualRotaryEncoder*>(ptr);
+ return virtualRotaryEncoder->writeScrollEvent(scrollAmount,
+ std::chrono::nanoseconds(eventTimeNanos));
+}
+
static JNINativeMethod methods[] = {
{"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)J",
(void*)nativeOpenUinputDpad},
@@ -392,6 +411,8 @@
(void*)nativeOpenUinputTouchscreen},
{"nativeOpenUinputStylus", "(Ljava/lang/String;IILjava/lang/String;II)J",
(void*)nativeOpenUinputStylus},
+ {"nativeOpenUinputRotaryEncoder", "(Ljava/lang/String;IILjava/lang/String;)J",
+ (void*)nativeOpenUinputRotaryEncoder},
{"nativeCloseUinput", "(J)V", (void*)nativeCloseUinput},
{"nativeWriteDpadKeyEvent", "(JIIJ)Z", (void*)nativeWriteDpadKeyEvent},
{"nativeWriteKeyEvent", "(JIIJ)Z", (void*)nativeWriteKeyEvent},
@@ -401,6 +422,8 @@
{"nativeWriteScrollEvent", "(JFFJ)Z", (void*)nativeWriteScrollEvent},
{"nativeWriteStylusMotionEvent", "(JIIIIIIIJ)Z", (void*)nativeWriteStylusMotionEvent},
{"nativeWriteStylusButtonEvent", "(JIIJ)Z", (void*)nativeWriteStylusButtonEvent},
+ {"nativeWriteRotaryEncoderScrollEvent", "(JFJ)Z",
+ (void*)nativeWriteRotaryEncoderScrollEvent},
};
int register_android_server_companion_virtual_InputController(JNIEnv* env) {
diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml
index 2ccd1e4..3b81f0a 100644
--- a/services/core/lint-baseline.xml
+++ b/services/core/lint-baseline.xml
@@ -145,4 +145,37 @@
line="7158"/>
</issue>
+ <issue
+ id="FlaggedApi"
+ message="Method `recordSmartReplied()` is a flagged API and should be inside an `if (Flags.lifetimeExtensionRefactor())` check (or annotate the surrounding method `onNotificationSmartReplySent` with `@FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR) to transfer requirement to caller`)"
+ errorLine1=" r.recordSmartReplied();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+ line="1591"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPackageImportanceWithIdentity()` is a flagged API and should be inside an `if (Flags.lifetimeExtensionRefactor())` check (or annotate the surrounding method `enqueueNotificationInternal` with `@FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR) to transfer requirement to caller`)"
+ errorLine1=" final int packageImportance = getPackageImportanceWithIdentity(pkg);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+ line="7546"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getPackageImportanceWithIdentity()` is a flagged API and should be inside an `if (Flags.lifetimeExtensionRefactor())` check (or annotate the surrounding method `onShortcutRemoved` with `@FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR) to transfer requirement to caller`)"
+ errorLine1=" final int packageImportance = getPackageImportanceWithIdentity(packageName);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+ line="7916"
+ column="51"/>
+ </issue>
+
</issues>
\ No newline at end of file
diff --git a/services/fakes/Android.bp b/services/fakes/Android.bp
index 148054b..d44bb5a 100644
--- a/services/fakes/Android.bp
+++ b/services/fakes/Android.bp
@@ -16,5 +16,5 @@
"java/**/*.java",
],
path: "java",
- visibility: ["//frameworks/base"],
+ visibility: ["//frameworks/base/ravenwood:__subpackages__"],
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 107c294..215cf2c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1924,7 +1924,11 @@
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
startTextToSpeechManagerService(context, t);
- startWearableSensingService(t);
+ if (!isWatch || !android.server.Flags.removeWearableSensingServiceFromWear()) {
+ startWearableSensingService(t);
+ } else {
+ Slog.d(TAG, "Not starting WearableSensingService");
+ }
startOnDeviceIntelligenceService(t);
if (deviceHasConfigString(
@@ -2640,9 +2644,13 @@
mSystemServiceManager.startService(MediaMetricsManagerService.class);
t.traceEnd();
- t.traceBegin("StartBackgroundInstallControlService");
- mSystemServiceManager.startService(BackgroundInstallControlService.class);
- t.traceEnd();
+ if (!com.android.server.flags.Flags.optionalBackgroundInstallControl()
+ || SystemProperties.getBoolean(
+ "ro.system_settings.service.backgound_install_control_enabled", true)) {
+ t.traceBegin("StartBackgroundInstallControlService");
+ mSystemServiceManager.startService(BackgroundInstallControlService.class);
+ t.traceEnd();
+ }
}
t.traceBegin("StartMediaProjectionManager");
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 38354e8..e8aa68c 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -14,4 +14,11 @@
namespace: "wear_frameworks"
description: "Remove TextServiceManagerService on Wear"
bug: "323720705"
+}
+
+flag {
+ name: "remove_wearable_sensing_service_from_wear"
+ namespace: "wear_frameworks"
+ description: "Remove WearableSensingManagerService on Wear"
+ bug: "340929916"
}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index bb0838d..c558aae 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1115,8 +1115,10 @@
}
private fun MutateStateScope.inheritImplicitPermissionStates(appId: Int, userId: Int) {
+ var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
val implicitPermissions = MutableIndexedSet<String>()
forEachPackageInAppId(appId) {
+ targetSdkVersion = targetSdkVersion.coerceAtMost(it.androidPackage!!.targetSdkVersion)
implicitPermissions += it.androidPackage!!.implicitPermissions
}
implicitPermissions.forEachIndexed implicitPermissions@{ _, implicitPermissionName ->
@@ -1153,7 +1155,10 @@
newFlags = newFlags or (sourceFlags and PermissionFlags.MASK_RUNTIME)
}
}
- if (implicitPermissionName in RETAIN_IMPLICIT_FLAGS_PERMISSIONS) {
+ if (
+ targetSdkVersion >= Build.VERSION_CODES.M &&
+ implicitPermissionName in NO_IMPLICIT_FLAG_PERMISSIONS
+ ) {
newFlags = newFlags andInv PermissionFlags.IMPLICIT
} else {
newFlags = newFlags or PermissionFlags.IMPLICIT
@@ -1782,7 +1787,7 @@
private const val PLATFORM_PACKAGE_NAME = "android"
// A set of permissions that we don't want to revoke when they are no longer implicit.
- private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS =
+ private val NO_IMPLICIT_FLAG_PERMISSIONS =
indexedSetOf(
Manifest.permission.ACCESS_MEDIA_LOCATION,
Manifest.permission.ACTIVITY_RECOGNITION,
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index cd70ed2..977a8a0 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -50,6 +50,7 @@
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@@ -378,10 +379,15 @@
@Override
public void onCameraOpened(String cameraId, String packageId) {
Log.d(LOG_TAG, "Received camera open event from: " + packageId);
- // Skip face auth and Android System Intelligence, since they trigger way too
- // often.
- if (packageId.startsWith("client.pid")
- || packageId.equals("com.google.android.as")) {
+ // Skip face auth since it triggers way too often.
+ if (packageId.startsWith("client.pid")) {
+ return;
+ }
+ // Additional vendor specific list of apps to skip.
+ String[] cameraSkipPackages =
+ getContext().getResources().getStringArray(
+ R.array.config_profcollectOnCameraOpenedSkipPackages);
+ if (Arrays.asList(cameraSkipPackages).contains(packageId)) {
return;
}
// Sample for a fraction of camera events.
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 0da17e1..3bce9b5 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -84,7 +84,6 @@
],
srcs: [
"src/com/android/server/inputmethod/**/ClientControllerTest.java",
- "src/com/android/server/inputmethod/**/UserDataRepositoryTest.java",
],
auto_gen_config: true,
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index a4ca317..d4adba2 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -87,7 +87,7 @@
synchronized (ImfLock.class) {
mVisibilityApplier.performShowIme(new Binder() /* showInputToken */,
ImeTracker.Token.empty(), 0 /* showFlags */, null /* resultReceiver */,
- SHOW_SOFT_INPUT);
+ SHOW_SOFT_INPUT, mUserId);
}
verifyShowSoftInput(false, true, 0 /* showFlags */);
}
@@ -96,7 +96,7 @@
public void testPerformHideIme() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.performHideIme(new Binder() /* hideInputToken */,
- ImeTracker.Token.empty(), null /* resultReceiver */, HIDE_SOFT_INPUT);
+ ImeTracker.Token.empty(), null /* resultReceiver */, HIDE_SOFT_INPUT, mUserId);
}
verifyHideSoftInput(false, true);
}
@@ -186,7 +186,7 @@
@Test
public void testShowImeScreenshot() {
synchronized (ImfLock.class) {
- mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY);
+ mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY, mUserId);
}
verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken),
@@ -196,7 +196,7 @@
@Test
public void testRemoveImeScreenshot() {
synchronized (ImfLock.class) {
- mVisibilityApplier.removeImeScreenshot(Display.DEFAULT_DISPLAY);
+ mVisibilityApplier.removeImeScreenshot(Display.DEFAULT_DISPLAY, mUserId);
}
verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY));
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index fbe384a..e81cf9d 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
@@ -28,60 +29,63 @@
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
public final class InputMethodSubtypeSwitchingControllerTest {
- private static final String DUMMY_PACKAGE_NAME = "dummy package name";
- private static final String DUMMY_IME_LABEL = "dummy ime label";
- private static final String DUMMY_SETTING_ACTIVITY_NAME = "";
- private static final boolean DUMMY_IS_AUX_IME = false;
- private static final boolean DUMMY_FORCE_DEFAULT = false;
- private static final boolean DUMMY_IS_VR_IME = false;
- private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
+ private static final String TEST_PACKAGE_NAME = "test package name";
+ private static final String TEST_IME_LABEL = "test ime label";
+ private static final String TEST_SETTING_ACTIVITY_NAME = "";
+ private static final boolean TEST_IS_AUX_IME = false;
+ private static final boolean TEST_FORCE_DEFAULT = false;
+ private static final boolean TEST_IS_VR_IME = false;
+ private static final int TEST_IS_DEFAULT_RES_ID = 0;
private static final String SYSTEM_LOCALE = "en_US";
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
- private static InputMethodSubtype createDummySubtype(final String locale) {
- final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
- return builder.setSubtypeNameResId(0)
+ @NonNull
+ private static InputMethodSubtype createTestSubtype(@NonNull String locale) {
+ return new InputMethodSubtypeBuilder()
+ .setSubtypeNameResId(0)
.setSubtypeIconResId(0)
.setSubtypeLocale(locale)
.setIsAsciiCapable(true)
.build();
}
- private static void addDummyImeSubtypeListItems(List<ImeSubtypeListItem> items,
- String imeName, String imeLabel, List<String> subtypeLocales,
- boolean supportsSwitchingToNextInputMethod) {
- final ResolveInfo ri = new ResolveInfo();
- final ServiceInfo si = new ServiceInfo();
+ private static void addTestImeSubtypeListItems(@NonNull List<ImeSubtypeListItem> items,
+ @NonNull String imeName, @NonNull String imeLabel,
+ @Nullable List<String> subtypeLocales, boolean supportsSwitchingToNextInputMethod) {
final ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = DUMMY_PACKAGE_NAME;
+ ai.packageName = TEST_PACKAGE_NAME;
ai.enabled = true;
+ final ServiceInfo si = new ServiceInfo();
si.applicationInfo = ai;
si.enabled = true;
- si.packageName = DUMMY_PACKAGE_NAME;
+ si.packageName = TEST_PACKAGE_NAME;
si.name = imeName;
si.exported = true;
si.nonLocalizedLabel = imeLabel;
+ final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
List<InputMethodSubtype> subtypes = null;
if (subtypeLocales != null) {
- subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes = new ArrayList<>();
for (String subtypeLocale : subtypeLocales) {
- subtypes.add(createDummySubtype(subtypeLocale));
+ subtypes.add(createTestSubtype(subtypeLocale));
}
}
- final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
- DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
- DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod, DUMMY_IS_VR_IME);
+ final InputMethodInfo imi = new InputMethodInfo(ri, TEST_IS_AUX_IME,
+ TEST_SETTING_ACTIVITY_NAME, subtypes, TEST_IS_DEFAULT_RES_ID,
+ TEST_FORCE_DEFAULT, supportsSwitchingToNextInputMethod, TEST_IS_VR_IME);
if (subtypes == null) {
items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
@@ -94,105 +98,104 @@
}
}
- private static ImeSubtypeListItem createDummyItem(ComponentName imeComponentName,
- String imeName, String subtypeName, String subtypeLocale, int subtypeIndex,
- String systemLocale) {
- final ResolveInfo ri = new ResolveInfo();
- final ServiceInfo si = new ServiceInfo();
- final ApplicationInfo ai = new ApplicationInfo();
+ @NonNull
+ private static ImeSubtypeListItem createTestItem(@NonNull ComponentName imeComponentName,
+ @NonNull String imeName, @NonNull String subtypeName,
+ @NonNull String subtypeLocale, int subtypeIndex) {
+ final var ai = new ApplicationInfo();
ai.packageName = imeComponentName.getPackageName();
ai.enabled = true;
+ final var si = new ServiceInfo();
si.applicationInfo = ai;
si.enabled = true;
si.packageName = imeComponentName.getPackageName();
si.name = imeComponentName.getClassName();
si.exported = true;
- si.nonLocalizedLabel = DUMMY_IME_LABEL;
+ si.nonLocalizedLabel = TEST_IME_LABEL;
+ final var ri = new ResolveInfo();
ri.serviceInfo = si;
- ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+ final var subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(new InputMethodSubtypeBuilder()
.setSubtypeNameResId(0)
.setSubtypeIconResId(0)
.setSubtypeLocale(subtypeLocale)
.setIsAsciiCapable(true)
.build());
- final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
- DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
- DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */,
- DUMMY_IS_VR_IME);
+ final InputMethodInfo imi = new InputMethodInfo(ri, TEST_IS_AUX_IME,
+ TEST_SETTING_ACTIVITY_NAME, subtypes, TEST_IS_DEFAULT_RES_ID,
+ TEST_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */, TEST_IS_VR_IME);
return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale,
- systemLocale);
+ SYSTEM_LOCALE);
}
+ @NonNull
private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
- final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
- addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
+ final var items = new ArrayList<ImeSubtypeListItem>();
+ addTestImeSubtypeListItems(items, "LatinIme", "LatinIme", List.of("en_US", "fr"),
true /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
- Arrays.asList("en_UK", "hi"),
+ addTestImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
+ List.of("en_UK", "hi"), false /* supportsSwitchingToNextInputMethod*/);
+ addTestImeSubtypeListItems(items, "subtypeAwareIme", "subtypeAwareIme", null,
+ true /* supportsSwitchingToNextInputMethod */);
+ addTestImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
false /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
- false /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"),
+ addTestImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", List.of("ja_JP"),
true /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
- Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
+ addTestImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
+ List.of("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
return items;
}
+ @NonNull
private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
- final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
- addDummyImeSubtypeListItems(items,
+ final var items = new ArrayList<ImeSubtypeListItem>();
+ addTestImeSubtypeListItems(items,
"UnknownIme", "UnknownIme",
- Arrays.asList("en_US", "hi"),
+ List.of("en_US", "hi"),
true /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items,
+ addTestImeSubtypeListItems(items,
"UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme",
- Arrays.asList("en_US"),
+ List.of("en_US"),
false /* supportsSwitchingToNextInputMethod*/);
- addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
+ addTestImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
"UnknownSubtypeUnawareIme", null,
false /* supportsSwitchingToNextInputMethod*/);
return items;
}
- private void assertNextInputMethod(final ControllerImpl controller,
- final boolean onlyCurrentIme,
- final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) {
+ private void assertNextInputMethod(@NonNull ControllerImpl controller, boolean onlyCurrentIme,
+ @NonNull ImeSubtypeListItem currentItem, @Nullable ImeSubtypeListItem nextItem) {
InputMethodSubtype subtype = null;
if (currentItem.mSubtypeName != null) {
- subtype = createDummySubtype(currentItem.mSubtypeName.toString());
+ subtype = createTestSubtype(currentItem.mSubtypeName.toString());
}
final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
currentItem.mImi, subtype);
assertEquals(nextItem, nextIme);
}
- private void assertRotationOrder(final ControllerImpl controller,
- final boolean onlyCurrentIme,
- final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
+ private void assertRotationOrder(@NonNull ControllerImpl controller, boolean onlyCurrentIme,
+ ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
final int numItems = expectedRotationOrderOfImeSubtypeList.length;
for (int i = 0; i < numItems; i++) {
- final int currentIndex = i;
- final int nextIndex = (currentIndex + 1) % numItems;
- final ImeSubtypeListItem currentItem =
- expectedRotationOrderOfImeSubtypeList[currentIndex];
+ final int nextIndex = (i + 1) % numItems;
+ final ImeSubtypeListItem currentItem = expectedRotationOrderOfImeSubtypeList[i];
final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex];
assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem);
}
}
- private void onUserAction(final ControllerImpl controller,
- final ImeSubtypeListItem subtypeListItem) {
+ private void onUserAction(@NonNull ControllerImpl controller,
+ @NonNull ImeSubtypeListItem subtypeListItem) {
InputMethodSubtype subtype = null;
if (subtypeListItem.mSubtypeName != null) {
- subtype = createDummySubtype(subtypeListItem.mSubtypeName.toString());
+ subtype = createTestSubtype(subtypeListItem.mSubtypeName.toString());
}
controller.onUserActionLocked(subtypeListItem.mImi, subtype);
}
@Test
- public void testControllerImpl() throws Exception {
+ public void testControllerImpl() {
final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
final ImeSubtypeListItem disabledIme_en_us = disabledItems.get(0);
final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
@@ -204,16 +207,17 @@
final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
final ImeSubtypeListItem switchingUnawareLatinIme_en_uk = enabledItems.get(2);
final ImeSubtypeListItem switchingUnawareLatinIme_hi = enabledItems.get(3);
- final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
- final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(5);
- final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(6);
+ final ImeSubtypeListItem subtypeAwareIme = enabledItems.get(4);
+ final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(5);
+ final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(6);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(7);
final ControllerImpl controller = ControllerImpl.createFrom(
null /* currentInstance */, enabledItems);
// switching-aware loop
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_en_us, latinIme_fr, japaneseIme_ja_jp);
+ latinIme_en_us, latinIme_fr, subtypeAwareIme, japaneseIme_ja_jp);
// switching-unaware loop
assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -226,6 +230,8 @@
assertRotationOrder(controller, true /* onlyCurrentIme */,
switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ subtypeAwareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
subtypeUnawareIme, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
japaneseIme_ja_jp, null);
@@ -252,59 +258,60 @@
}
@Test
- public void testControllerImplWithUserAction() throws Exception {
+ public void testControllerImplWithUserAction() {
final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
final ImeSubtypeListItem latinIme_en_us = enabledItems.get(0);
final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
- final ImeSubtypeListItem switchingUnawarelatinIme_en_uk = enabledItems.get(2);
- final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
- final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
- final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(5);
- final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(6);
+ final ImeSubtypeListItem switchingUnawareLatinIme_en_uk = enabledItems.get(2);
+ final ImeSubtypeListItem switchingUnawareLatinIme_hi = enabledItems.get(3);
+ final ImeSubtypeListItem subtypeAwareIme = enabledItems.get(4);
+ final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(5);
+ final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(6);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(7);
final ControllerImpl controller = ControllerImpl.createFrom(
null /* currentInstance */, enabledItems);
// === switching-aware loop ===
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_en_us, latinIme_fr, japaneseIme_ja_jp);
+ latinIme_en_us, latinIme_fr, subtypeAwareIme, japaneseIme_ja_jp);
// Then notify that a user did something for latinIme_fr.
onUserAction(controller, latinIme_fr);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
+ latinIme_fr, latinIme_en_us, subtypeAwareIme, japaneseIme_ja_jp);
// Then notify that a user did something for latinIme_fr again.
onUserAction(controller, latinIme_fr);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
- // Then notify that a user did something for japaneseIme_ja_JP.
- onUserAction(controller, latinIme_fr);
+ latinIme_fr, latinIme_en_us, subtypeAwareIme, japaneseIme_ja_jp);
+ // Then notify that a user did something for subtypeAwareIme.
+ onUserAction(controller, subtypeAwareIme);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- japaneseIme_ja_jp, latinIme_fr, latinIme_en_us);
+ subtypeAwareIme, latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
// Check onlyCurrentIme == true.
- assertNextInputMethod(controller, true /* onlyCurrentIme */,
- japaneseIme_ja_jp, null);
assertRotationOrder(controller, true /* onlyCurrentIme */,
latinIme_fr, latinIme_en_us);
- assertRotationOrder(controller, true /* onlyCurrentIme */,
- latinIme_en_us, latinIme_fr);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ subtypeAwareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ japaneseIme_ja_jp, null);
// === switching-unaware loop ===
assertRotationOrder(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi, subtypeUnawareIme,
switchUnawareJapaneseIme_ja_jp);
// User action should be ignored for switching unaware IMEs.
- onUserAction(controller, switchingUnawarelatinIme_hi);
+ onUserAction(controller, switchingUnawareLatinIme_hi);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi, subtypeUnawareIme,
switchUnawareJapaneseIme_ja_jp);
// User action should be ignored for switching unaware IMEs.
- onUserAction(controller, switchUnawareJapaneseIme_ja_jp);
+ onUserAction(controller, subtypeUnawareIme);
assertRotationOrder(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi, subtypeUnawareIme,
switchUnawareJapaneseIme_ja_jp);
// Check onlyCurrentIme == true.
assertRotationOrder(controller, true /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi);
+ switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
subtypeUnawareIme, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
@@ -315,40 +322,41 @@
final ControllerImpl newController = ControllerImpl.createFrom(controller,
sameEnabledItems);
assertRotationOrder(newController, false /* onlyCurrentIme */,
- japaneseIme_ja_jp, latinIme_fr, latinIme_en_us);
+ subtypeAwareIme, latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
assertRotationOrder(newController, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_uk, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi, subtypeUnawareIme,
switchUnawareJapaneseIme_ja_jp);
// Rotation order should be initialized when created with a different subtype list.
- final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList(
- latinIme_en_us, latinIme_fr, switchingUnawarelatinIme_en_uk,
- switchUnawareJapaneseIme_ja_jp);
+ final List<ImeSubtypeListItem> differentEnabledItems = List.of(
+ latinIme_en_us, latinIme_fr, subtypeAwareIme, switchingUnawareLatinIme_en_uk,
+ switchUnawareJapaneseIme_ja_jp, subtypeUnawareIme);
final ControllerImpl anotherController = ControllerImpl.createFrom(controller,
differentEnabledItems);
assertRotationOrder(anotherController, false /* onlyCurrentIme */,
- latinIme_en_us, latinIme_fr);
+ latinIme_en_us, latinIme_fr, subtypeAwareIme);
assertRotationOrder(anotherController, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_uk, switchUnawareJapaneseIme_ja_jp);
+ switchingUnawareLatinIme_en_uk, switchUnawareJapaneseIme_ja_jp, subtypeUnawareIme);
}
@Test
- public void testImeSubtypeListItem() throws Exception {
- final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
- addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme",
- Arrays.asList("en_US", "fr", "en", "en_uk", "enn", "e", "EN_US"),
+ public void testImeSubtypeListItem() {
+ final var items = new ArrayList<ImeSubtypeListItem>();
+ addTestImeSubtypeListItems(items, "LatinIme", "LatinIme",
+ List.of("en_US", "fr", "en", "en_uk", "enn", "e", "EN_US"),
true /* supportsSwitchingToNextInputMethod*/);
final ImeSubtypeListItem item_en_us = items.get(0);
final ImeSubtypeListItem item_fr = items.get(1);
final ImeSubtypeListItem item_en = items.get(2);
- final ImeSubtypeListItem item_enn = items.get(3);
- final ImeSubtypeListItem item_e = items.get(4);
- final ImeSubtypeListItem item_en_us_allcaps = items.get(5);
+ final ImeSubtypeListItem item_en_uk = items.get(3);
+ final ImeSubtypeListItem item_enn = items.get(4);
+ final ImeSubtypeListItem item_e = items.get(5);
+ final ImeSubtypeListItem item_en_us_allcaps = items.get(6);
assertTrue(item_en_us.mIsSystemLocale);
assertFalse(item_fr.mIsSystemLocale);
assertFalse(item_en.mIsSystemLocale);
- assertFalse(item_en.mIsSystemLocale);
+ assertFalse(item_en_uk.mIsSystemLocale);
assertFalse(item_enn.mIsSystemLocale);
assertFalse(item_e.mIsSystemLocale);
assertFalse(item_en_us_allcaps.mIsSystemLocale);
@@ -356,80 +364,81 @@
assertTrue(item_en_us.mIsSystemLanguage);
assertFalse(item_fr.mIsSystemLanguage);
assertTrue(item_en.mIsSystemLanguage);
- assertFalse(item_enn.mIsSystemLocale);
- assertFalse(item_e.mIsSystemLocale);
- assertFalse(item_en_us_allcaps.mIsSystemLocale);
+ assertTrue(item_en_uk.mIsSystemLanguage);
+ assertFalse(item_enn.mIsSystemLanguage);
+ assertFalse(item_e.mIsSystemLanguage);
+ assertFalse(item_en_us_allcaps.mIsSystemLanguage);
}
@SuppressWarnings("SelfComparison")
@Test
- public void testImeSubtypeListComparator() throws Exception {
+ public void testImeSubtypeListComparator() {
final ComponentName imeX1 = new ComponentName("com.example.imeX", "Ime1");
final ComponentName imeX2 = new ComponentName("com.example.imeX", "Ime2");
final ComponentName imeY1 = new ComponentName("com.example.imeY", "Ime1");
final ComponentName imeZ1 = new ComponentName("com.example.imeZ", "Ime1");
{
- final List<ImeSubtypeListItem> items = Arrays.asList(
+ final List<ImeSubtypeListItem> items = List.of(
// Subtypes of two IMEs that have the same display name "X".
// Subtypes that has the same locale of the system's.
- createDummyItem(imeX1, "X", "E", "en_US", 0, "en_US"),
- createDummyItem(imeX2, "X", "E", "en_US", 0, "en_US"),
- createDummyItem(imeX1, "X", "Z", "en_US", 3, "en_US"),
- createDummyItem(imeX2, "X", "Z", "en_US", 3, "en_US"),
- createDummyItem(imeX1, "X", "", "en_US", 6, "en_US"),
- createDummyItem(imeX2, "X", "", "en_US", 6, "en_US"),
+ createTestItem(imeX1, "X", "E", "en_US", 0),
+ createTestItem(imeX2, "X", "E", "en_US", 0),
+ createTestItem(imeX1, "X", "Z", "en_US", 3),
+ createTestItem(imeX2, "X", "Z", "en_US", 3),
+ createTestItem(imeX1, "X", "", "en_US", 6),
+ createTestItem(imeX2, "X", "", "en_US", 6),
// Subtypes that has the same language of the system's.
- createDummyItem(imeX1, "X", "E", "en", 1, "en_US"),
- createDummyItem(imeX2, "X", "E", "en", 1, "en_US"),
- createDummyItem(imeX1, "X", "Z", "en", 4, "en_US"),
- createDummyItem(imeX2, "X", "Z", "en", 4, "en_US"),
- createDummyItem(imeX1, "X", "", "en", 7, "en_US"),
- createDummyItem(imeX2, "X", "", "en", 7, "en_US"),
+ createTestItem(imeX1, "X", "E", "en", 1),
+ createTestItem(imeX2, "X", "E", "en", 1),
+ createTestItem(imeX1, "X", "Z", "en", 4),
+ createTestItem(imeX2, "X", "Z", "en", 4),
+ createTestItem(imeX1, "X", "", "en", 7),
+ createTestItem(imeX2, "X", "", "en", 7),
// Subtypes that has different language than the system's.
- createDummyItem(imeX1, "X", "A", "hi_IN", 27, "en_US"),
- createDummyItem(imeX2, "X", "A", "hi_IN", 27, "en_US"),
- createDummyItem(imeX1, "X", "E", "ja", 2, "en_US"),
- createDummyItem(imeX2, "X", "E", "ja", 2, "en_US"),
- createDummyItem(imeX1, "X", "Z", "ja", 5, "en_US"),
- createDummyItem(imeX2, "X", "Z", "ja", 5, "en_US"),
- createDummyItem(imeX1, "X", "", "ja", 8, "en_US"),
- createDummyItem(imeX2, "X", "", "ja", 8, "en_US"),
+ createTestItem(imeX1, "X", "A", "hi_IN", 27),
+ createTestItem(imeX2, "X", "A", "hi_IN", 27),
+ createTestItem(imeX1, "X", "E", "ja", 2),
+ createTestItem(imeX2, "X", "E", "ja", 2),
+ createTestItem(imeX1, "X", "Z", "ja", 5),
+ createTestItem(imeX2, "X", "Z", "ja", 5),
+ createTestItem(imeX1, "X", "", "ja", 8),
+ createTestItem(imeX2, "X", "", "ja", 8),
// Subtypes of IME "Y".
// Subtypes that has the same locale of the system's.
- createDummyItem(imeY1, "Y", "E", "en_US", 9, "en_US"),
- createDummyItem(imeY1, "Y", "Z", "en_US", 12, "en_US"),
- createDummyItem(imeY1, "Y", "", "en_US", 15, "en_US"),
+ createTestItem(imeY1, "Y", "E", "en_US", 9),
+ createTestItem(imeY1, "Y", "Z", "en_US", 12),
+ createTestItem(imeY1, "Y", "", "en_US", 15),
// Subtypes that has the same language of the system's.
- createDummyItem(imeY1, "Y", "E", "en", 10, "en_US"),
- createDummyItem(imeY1, "Y", "Z", "en", 13, "en_US"),
- createDummyItem(imeY1, "Y", "", "en", 16, "en_US"),
+ createTestItem(imeY1, "Y", "E", "en", 10),
+ createTestItem(imeY1, "Y", "Z", "en", 13),
+ createTestItem(imeY1, "Y", "", "en", 16),
// Subtypes that has different language than the system's.
- createDummyItem(imeY1, "Y", "A", "hi_IN", 28, "en_US"),
- createDummyItem(imeY1, "Y", "E", "ja", 11, "en_US"),
- createDummyItem(imeY1, "Y", "Z", "ja", 14, "en_US"),
- createDummyItem(imeY1, "Y", "", "ja", 17, "en_US"),
+ createTestItem(imeY1, "Y", "A", "hi_IN", 28),
+ createTestItem(imeY1, "Y", "E", "ja", 11),
+ createTestItem(imeY1, "Y", "Z", "ja", 14),
+ createTestItem(imeY1, "Y", "", "ja", 17),
// Subtypes of IME Z.
// Subtypes that has the same locale of the system's.
- createDummyItem(imeZ1, "", "E", "en_US", 18, "en_US"),
- createDummyItem(imeZ1, "", "Z", "en_US", 21, "en_US"),
- createDummyItem(imeZ1, "", "", "en_US", 24, "en_US"),
+ createTestItem(imeZ1, "", "E", "en_US", 18),
+ createTestItem(imeZ1, "", "Z", "en_US", 21),
+ createTestItem(imeZ1, "", "", "en_US", 24),
// Subtypes that has the same language of the system's.
- createDummyItem(imeZ1, "", "E", "en", 19, "en_US"),
- createDummyItem(imeZ1, "", "Z", "en", 22, "en_US"),
- createDummyItem(imeZ1, "", "", "en", 25, "en_US"),
+ createTestItem(imeZ1, "", "E", "en", 19),
+ createTestItem(imeZ1, "", "Z", "en", 22),
+ createTestItem(imeZ1, "", "", "en", 25),
// Subtypes that has different language than the system's.
- createDummyItem(imeZ1, "", "A", "hi_IN", 29, "en_US"),
- createDummyItem(imeZ1, "", "E", "ja", 20, "en_US"),
- createDummyItem(imeZ1, "", "Z", "ja", 23, "en_US"),
- createDummyItem(imeZ1, "", "", "ja", 26, "en_US"));
+ createTestItem(imeZ1, "", "A", "hi_IN", 29),
+ createTestItem(imeZ1, "", "E", "ja", 20),
+ createTestItem(imeZ1, "", "Z", "ja", 23),
+ createTestItem(imeZ1, "", "", "ja", 26));
// Ensure {@link java.lang.Comparable#compareTo} contracts are satisfied.
for (int i = 0; i < items.size(); ++i) {
final ImeSubtypeListItem item1 = items.get(i);
// Ensures sgn(x.compareTo(y)) == -sgn(y.compareTo(x)).
- assertTrue(item1 + " has the same order of itself", item1.compareTo(item1) == 0);
+ assertEquals(item1 + " has the same order of itself", 0, item1.compareTo(item1));
// Ensures (x.compareTo(y) > 0 && y.compareTo(z) > 0) implies x.compareTo(z) > 0.
for (int j = i + 1; j < items.size(); ++j) {
final ImeSubtypeListItem item2 = items.get(j);
@@ -442,26 +451,24 @@
{
// Following two items have the same priority.
- final ImeSubtypeListItem nonSystemLocale1 =
- createDummyItem(imeX1, "X", "A", "ja_JP", 0, "en_US");
- final ImeSubtypeListItem nonSystemLocale2 =
- createDummyItem(imeX1, "X", "A", "hi_IN", 1, "en_US");
- assertTrue(nonSystemLocale1.compareTo(nonSystemLocale2) == 0);
- assertTrue(nonSystemLocale2.compareTo(nonSystemLocale1) == 0);
+ final ImeSubtypeListItem nonSystemLocale1 = createTestItem(imeX1, "X", "A", "ja_JP", 0);
+ final ImeSubtypeListItem nonSystemLocale2 = createTestItem(imeX1, "X", "A", "hi_IN", 1);
+ assertEquals(0, nonSystemLocale1.compareTo(nonSystemLocale2));
+ assertEquals(0, nonSystemLocale2.compareTo(nonSystemLocale1));
// But those aren't equal to each other.
- assertFalse(nonSystemLocale1.equals(nonSystemLocale2));
- assertFalse(nonSystemLocale2.equals(nonSystemLocale1));
+ assertNotEquals(nonSystemLocale1, nonSystemLocale2);
+ assertNotEquals(nonSystemLocale2, nonSystemLocale1);
}
{
// Check if ComponentName is also taken into account when comparing two items.
- final ImeSubtypeListItem ime1 = createDummyItem(imeX1, "X", "A", "ja_JP", 0, "en_US");
- final ImeSubtypeListItem ime2 = createDummyItem(imeX2, "X", "A", "ja_JP", 0, "en_US");
+ final ImeSubtypeListItem ime1 = createTestItem(imeX1, "X", "A", "ja_JP", 0);
+ final ImeSubtypeListItem ime2 = createTestItem(imeX2, "X", "A", "ja_JP", 0);
assertTrue(ime1.compareTo(ime2) < 0);
assertTrue(ime2.compareTo(ime1) > 0);
// But those aren't equal to each other.
- assertFalse(ime1.equals(ime2));
- assertFalse(ime2.equals(ime1));
+ assertNotEquals(ime1, ime2);
+ assertNotEquals(ime2, ime1);
}
}
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
index c3a87da..f9f45057 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/UserDataRepositoryTest.java
@@ -30,6 +30,7 @@
import com.android.server.pm.UserManagerInternal;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -63,6 +64,8 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ SecureSettingsWrapper.startTestMode();
+
mHandler = new Handler(Looper.getMainLooper());
mBindingControllerFactory = new IntFunction<InputMethodBindingController>() {
@@ -73,13 +76,18 @@
};
}
+ @After
+ public void tearDown() {
+ SecureSettingsWrapper.endTestMode();
+ }
+
@Test
public void testUserDataRepository_addsNewUserInfoOnUserCreatedEvent() {
// Create UserDataRepository and capture the user lifecycle listener
final var captor = ArgumentCaptor.forClass(UserManagerInternal.UserLifecycleListener.class);
final var bindingControllerFactorySpy = spy(mBindingControllerFactory);
- final var repository = new UserDataRepository(mHandler, mMockUserManagerInternal,
- bindingControllerFactorySpy);
+ final var repository = new UserDataRepository(mHandler,
+ mMockUserManagerInternal, bindingControllerFactorySpy);
verify(mMockUserManagerInternal, times(1)).addUserLifecycleListener(captor.capture());
final var listener = captor.getValue();
@@ -105,7 +113,8 @@
public void testUserDataRepository_removesUserInfoOnUserRemovedEvent() {
// Create UserDataRepository and capture the user lifecycle listener
final var captor = ArgumentCaptor.forClass(UserManagerInternal.UserLifecycleListener.class);
- final var repository = new UserDataRepository(mHandler, mMockUserManagerInternal,
+ final var repository = new UserDataRepository(mHandler,
+ mMockUserManagerInternal,
userId -> new InputMethodBindingController(userId, mMockInputMethodManagerService));
verify(mMockUserManagerInternal, times(1)).addUserLifecycleListener(captor.capture());
@@ -127,8 +136,8 @@
@Test
public void testGetOrCreate() {
- final var repository = new UserDataRepository(mHandler, mMockUserManagerInternal,
- mBindingControllerFactory);
+ final var repository = new UserDataRepository(mHandler,
+ mMockUserManagerInternal, mBindingControllerFactory);
synchronized (ImfLock.class) {
final var userData = repository.getOrCreate(ANY_USER_ID);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
index 4409051..30c384a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -94,4 +94,11 @@
verify(mDisplayOffloader).onBlockingScreenOn(eq(unblocker));
}
+
+ @Test
+ public void testUnblockScreenOn() {
+ mSession.cancelBlockScreenOn();
+
+ verify(mDisplayOffloader).cancelBlockScreenOn();
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 95f0b65..bb774ee 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -105,7 +105,6 @@
import java.util.List;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class DisplayPowerControllerTest {
@@ -1660,6 +1659,8 @@
int initState = Display.STATE_OFF;
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ when(mDisplayOffloadSession.blockScreenOn(any())).thenReturn(true);
+
// start with OFF.
when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1673,6 +1674,7 @@
advanceTime(1); // Run updatePowerState
verify(mDisplayOffloadSession).blockScreenOn(any(Runnable.class));
+ verify(mDisplayOffloadSession, never()).cancelBlockScreenOn();
}
@Test
@@ -1680,6 +1682,8 @@
// set up.
int initState = Display.STATE_ON;
mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ when(mDisplayOffloadSession.blockScreenOn(any())).thenReturn(true);
+
// start with ON.
when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -1692,7 +1696,78 @@
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
- verify(mDisplayOffloadSession, never()).blockScreenOn(any(Runnable.class));
+ // No cancelBlockScreenOn call because we didn't block.
+ verify(mDisplayOffloadSession, never()).cancelBlockScreenOn();
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_OFFLOAD_SESSION_CANCEL_BLOCK_SCREEN_ON)
+ @Test
+ public void testOffloadBlocker_turnON_thenOFF_cancelBlockScreenOnNotCalledIfUnblocked() {
+ // Set up.
+ int initState = Display.STATE_OFF;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ when(mDisplayOffloadSession.blockScreenOn(any())).thenReturn(true);
+
+ // Start with OFF.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // Go to ON.
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mDisplayOffloadSession).blockScreenOn(argumentCaptor.capture());
+
+ // Unblocked
+ argumentCaptor.getValue().run();
+ advanceTime(1); // Run updatePowerState
+
+ // Go to OFF immediately
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // No cancelBlockScreenOn call because we already unblocked
+ verify(mDisplayOffloadSession, never()).cancelBlockScreenOn();
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_OFFLOAD_SESSION_CANCEL_BLOCK_SCREEN_ON)
+ @Test
+ public void testOffloadBlocker_turnON_thenOFF_cancelBlockScreenOn() {
+ // Set up.
+ int initState = Display.STATE_OFF;
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ when(mDisplayOffloadSession.blockScreenOn(any())).thenReturn(true);
+
+ // Start with OFF.
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // Go to ON.
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // We should call blockScreenOn
+ verify(mDisplayOffloadSession).blockScreenOn(any(Runnable.class));
+
+ // Go to OFF immediately
+ dpr.policy = DisplayPowerRequest.POLICY_OFF;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // We should call cancelBlockScreenOn
+ verify(mDisplayOffloadSession).cancelBlockScreenOn();
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 01ff35f..a7e0ebd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -33,7 +33,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
@@ -83,7 +82,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LocalDisplayAdapterTest {
@@ -126,7 +124,7 @@
private DisplayOffloadSessionImpl mDisplayOffloadSession;
- private DisplayOffloader mDisplayOffloader;
+ @Mock DisplayOffloader mDisplayOffloader;
private TestListener mListener = new TestListener();
@@ -1249,24 +1247,8 @@
}
private void initDisplayOffloadSession() {
- mDisplayOffloader = spy(new DisplayOffloader() {
- @Override
- public boolean startOffload() {
- return true;
- }
-
- @Override
- public void stopOffload() {}
-
- @Override
- public void onBlockingScreenOn(Runnable unblocker) {}
-
- @Override
- public boolean allowAutoBrightnessInDoze() {
- return true;
- }
- });
-
+ when(mDisplayOffloader.startOffload()).thenReturn(true);
+ when(mDisplayOffloader.allowAutoBrightnessInDoze()).thenReturn(true);
mDisplayOffloadSession = new DisplayOffloadSessionImpl(mDisplayOffloader,
mMockedDisplayPowerController);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index 88d3238..d7936fe 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -255,7 +256,8 @@
@Test
public void setBrightnessSetsInBrightnessSetting() {
float brightnessValue = 0.3f;
- mDisplayBrightnessController.setBrightness(brightnessValue);
+ float maxBrightnessValue = 0.65f;
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightnessValue);
verify(mBrightnessSetting).setBrightness(brightnessValue);
}
@@ -266,20 +268,24 @@
// Sets the appropriate value when valid, and not equal to the current brightness
float brightnessValue = 0.3f;
- mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ float maxBrightnessValue = 0.65f;
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue,
+ maxBrightnessValue);
assertEquals(mDisplayBrightnessController.getCurrentBrightness(), brightnessValue, 0.0f);
verify(mBrightnessChangeExecutor).execute(mOnBrightnessChangeRunnable);
verify(mBrightnessSetting).setBrightness(brightnessValue);
// Does nothing if the value is invalid
- mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN);
+ clearInvocations(mBrightnessSetting);
+ mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN, maxBrightnessValue);
verifyNoMoreInteractions(mBrightnessChangeExecutor, mBrightnessSetting);
// Does nothing if the value is same as the current brightness
brightnessValue = 0.2f;
mDisplayBrightnessController.setAndNotifyCurrentScreenBrightness(brightnessValue);
verify(mBrightnessChangeExecutor, times(2)).execute(mOnBrightnessChangeRunnable);
- mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue);
+ mDisplayBrightnessController.updateScreenBrightnessSetting(brightnessValue,
+ maxBrightnessValue);
verifyNoMoreInteractions(mBrightnessChangeExecutor, mBrightnessSetting);
}
@@ -366,7 +372,7 @@
when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(nits);
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
- verify(mBrightnessSetting).setBrightness(brightness);
+ verify(mBrightnessSetting).setBrightnessNoNotify(brightness);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
clearInvocations(automaticBrightnessController, mBrightnessSetting);
@@ -378,7 +384,7 @@
when(mBrightnessSetting.getBrightness()).thenReturn(brightness);
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
- verify(mBrightnessSetting, never()).setBrightness(brightness);
+ verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
clearInvocations(automaticBrightnessController, mBrightnessSetting);
@@ -395,15 +401,73 @@
assertEquals(brightness, mDisplayBrightnessController.getCurrentBrightness(), 0.01f);
verifyZeroInteractions(automaticBrightnessController);
verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay();
- verify(mBrightnessSetting, never()).setBrightness(brightness);
+ verify(mBrightnessSetting, never()).setBrightnessNoNotify(brightness);
}
@Test
+ public void testDoesNotSetBrightnessNits_settingMaxBrightnessAndStoredValueGreater() {
+ float brightnessValue = 0.65f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 300f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting, never()).setBrightnessNitsForDefaultDisplay(anyFloat());
+ }
+
+ @Test
+ public void testSetsBrightnessNits_storedValueLower() {
+ float brightnessValue = 0.65f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 100f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits);
+ }
+
+ @Test
+ public void testSetsBrightnessNits_lowerThanMax() {
+ float brightnessValue = 0.60f;
+ float maxBrightness = 0.65f;
+ float nits = 200f;
+ float storedNits = 300f;
+
+ when(mBrightnessSetting.getBrightnessNitsForDefaultDisplay()).thenReturn(storedNits);
+ AutomaticBrightnessController automaticBrightnessController = mock(
+ AutomaticBrightnessController.class);
+ when(automaticBrightnessController.convertToNits(brightnessValue)).thenReturn(nits);
+ mDisplayBrightnessController.mAutomaticBrightnessController = automaticBrightnessController;
+
+ mDisplayBrightnessController.setBrightness(brightnessValue, maxBrightness);
+
+ verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits);
+ }
+
+
+ @Test
public void testChangeBrightnessNitsWhenUserChanges() {
float brightnessValue1 = 0.3f;
float nits1 = 200f;
+ int userSerial1 = 1;
float brightnessValue2 = 0.5f;
float nits2 = 300f;
+ int userSerial2 = 2;
+ float maxBrightness = 0.65f;
AutomaticBrightnessController automaticBrightnessController = mock(
AutomaticBrightnessController.class);
when(automaticBrightnessController.convertToNits(brightnessValue1)).thenReturn(nits1);
@@ -417,13 +481,13 @@
verify(automaticBrightnessStrategy)
.setAutomaticBrightnessController(automaticBrightnessController);
- mDisplayBrightnessController.setBrightness(brightnessValue1, 1 /* user-serial */);
- verify(mBrightnessSetting).setUserSerial(1);
+ mDisplayBrightnessController.setBrightness(brightnessValue1, userSerial1, maxBrightness);
+ verify(mBrightnessSetting).setUserSerial(userSerial1);
verify(mBrightnessSetting).setBrightness(brightnessValue1);
verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits1);
- mDisplayBrightnessController.setBrightness(brightnessValue2, 2 /* user-serial */);
- verify(mBrightnessSetting).setUserSerial(2);
+ mDisplayBrightnessController.setBrightness(brightnessValue2, userSerial2, maxBrightness);
+ verify(mBrightnessSetting).setUserSerial(userSerial2);
verify(mBrightnessSetting).setBrightness(brightnessValue2);
verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 69043f5..e982153 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -16,19 +16,19 @@
package com.android.server.display.brightness.clamper;
+import static android.view.Display.STATE_OFF;
import static android.view.Display.STATE_ON;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal;
@@ -40,12 +40,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig;
-import com.android.server.display.TestUtils;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.OffsettableClock;
@@ -63,6 +61,7 @@
@SmallTest
public class BrightnessClamperControllerTest {
private static final float FLOAT_TOLERANCE = 0.001f;
+ private static final int DISPLAY_ID = 2;
private final OffsettableClock mClock = new OffsettableClock();
private final TestHandler mTestHandler = new TestHandler(null, mClock);
@@ -78,8 +77,12 @@
@Mock
private BrightnessClamperController.DisplayDeviceData mMockDisplayDeviceData;
@Mock
+ private SensorData mMockSensorData;
+ @Mock
private DeviceConfigParameterProvider mMockDeviceConfigParameterProvider;
@Mock
+ private LightSensorController mMockLightSensorController;
+ @Mock
private BrightnessClamper<BrightnessClamperController.DisplayDeviceData> mMockClamper;
@Mock
private DisplayManagerFlags mFlags;
@@ -88,23 +91,17 @@
@Mock
private DisplayManagerInternal.DisplayPowerRequest mMockRequest;
- Sensor mLightSensor;
-
@Mock
private DeviceConfig.Properties mMockProperties;
private BrightnessClamperController mClamperController;
private TestInjector mTestInjector;
- @Rule
- public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
mTestInjector = new TestInjector(List.of(mMockClamper), List.of(mMockModifier));
- when(mSensorManager.getDefaultSensor(anyInt())).thenReturn(mLightSensor);
- when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+ when(mMockDisplayDeviceData.getDisplayId()).thenReturn(DISPLAY_ID);
+ when(mMockDisplayDeviceData.getAmbientLightSensor()).thenReturn(mMockSensorData);
mClamperController = createBrightnessClamperController();
}
@@ -115,6 +112,25 @@
}
@Test
+ public void testConstructor_ConfiguresLightSensorController() {
+ verify(mMockLightSensorController).configure(mMockSensorData, DISPLAY_ID);
+ }
+
+ @Test
+ public void testConstructor_doesNotStartsLightSensorController() {
+ verify(mMockLightSensorController, never()).restart();
+ }
+
+ @Test
+ public void testConstructor_startsLightSensorController() {
+ when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+
+ mClamperController = createBrightnessClamperController();
+
+ verify(mMockLightSensorController).restart();
+ }
+
+ @Test
public void testStop_RemovesOnPropertiesChangeListener() {
ArgumentCaptor<DeviceConfig.OnPropertiesChangedListener> captor = ArgumentCaptor.forClass(
DeviceConfig.OnPropertiesChangedListener.class);
@@ -152,6 +168,21 @@
}
@Test
+ public void testOnDisplayChanged_doesNotRestartLightSensor() {
+ mClamperController.onDisplayChanged(mMockDisplayDeviceData);
+
+ verify(mMockLightSensorController, never()).restart();
+ }
+
+ @Test
+ public void testOnDisplayChanged_restartsLightSensor() {
+ when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+ mClamperController.onDisplayChanged(mMockDisplayDeviceData);
+
+ verify(mMockLightSensorController).restart();
+ }
+
+ @Test
public void testClamp_AppliesModifier() {
float initialBrightness = 0.2f;
boolean initialSlowChange = true;
@@ -161,6 +192,26 @@
}
@Test
+ public void testClamp_restartsLightSensor() {
+ float initialBrightness = 0.2f;
+ boolean initialSlowChange = true;
+ when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+ mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
+
+ verify(mMockLightSensorController).restart();
+ }
+
+ @Test
+ public void testClamp_stopsLightSensor() {
+ float initialBrightness = 0.2f;
+ boolean initialSlowChange = true;
+ clearInvocations(mMockLightSensorController);
+ mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_OFF);
+
+ verify(mMockLightSensorController).stop();
+ }
+
+ @Test
public void testClamp_inactiveClamperNotApplied() {
float initialBrightness = 0.8f;
boolean initialSlowChange = true;
@@ -260,33 +311,17 @@
}
@Test
- public void testAmbientLuxChanges() throws Exception {
- ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
- SensorEventListener.class);
+ public void testAmbientLuxChanges() {
+ mTestInjector.mCapturedLightSensorListener.onAmbientLuxChange(50);
- verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
- anyInt(), any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL))).thenReturn(List.of(mLightSensor));
-
- float initialBrightness = 0.8f;
- boolean initialSlowChange = true;
-
- DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange, STATE_ON);
- assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
-
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 50, mClock.now()));
verify(mMockModifier).setAmbientLux(50);
-
- listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 300, mClock.now()));
- verify(mMockModifier).setAmbientLux(300);
}
@Test
public void testStop() {
+ clearInvocations(mMockLightSensorController);
mClamperController.stop();
+ verify(mMockLightSensorController).stop();
verify(mMockModifier).stop();
verify(mMockClamper).stop();
}
@@ -303,6 +338,7 @@
private final List<BrightnessStateModifier> mModifiers;
private BrightnessClamperController.ClamperChangeListener mCapturedChangeListener;
+ private LightSensorController.LightSensorListener mCapturedLightSensorListener;
private TestInjector(
List<BrightnessClamper<? super BrightnessClamperController.DisplayDeviceData>>
@@ -330,8 +366,15 @@
@Override
List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
Handler handler, BrightnessClamperController.ClamperChangeListener listener,
- DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) {
+ DisplayDeviceConfig displayDeviceConfig) {
return mModifiers;
}
+
+ @Override
+ LightSensorController getLightSensorController(SensorManager sensorManager, Context context,
+ LightSensorController.LightSensorListener listener, Handler handler) {
+ mCapturedLightSensorListener = listener;
+ return mMockLightSensorController;
+ }
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/LightSensorControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/LightSensorControllerTest.kt
new file mode 100644
index 0000000..b742d02
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/LightSensorControllerTest.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.clamper
+
+import android.content.res.Resources
+import android.hardware.Sensor
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import android.os.Handler
+import androidx.test.filters.SmallTest
+import com.android.server.display.TestUtils
+import com.android.server.display.brightness.clamper.LightSensorController.Injector
+import com.android.server.display.brightness.clamper.LightSensorController.LightSensorListener
+import com.android.server.display.config.SensorData
+import com.android.server.display.utils.AmbientFilter
+import org.junit.Before
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.inOrder
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
+
+private const val LIGHT_SENSOR_RATE: Int = 10
+private const val DISPLAY_ID: Int = 3
+private const val NOW: Long = 3_000
+
+@SmallTest
+class LightSensorControllerTest {
+
+ private val mockSensorManager: SensorManager = mock()
+ private val mockResources: Resources = mock()
+ private val mockLightSensorListener: LightSensorListener = mock()
+ private val mockHandler: Handler = mock()
+ private val mockAmbientFilter: AmbientFilter = mock()
+
+ private val testInjector = TestInjector()
+ private val dummySensorData = SensorData()
+
+ private lateinit var controller: LightSensorController
+
+ @Before
+ fun setUp() {
+ controller = LightSensorController(mockSensorManager, mockResources,
+ mockLightSensorListener, mockHandler, testInjector)
+ }
+
+ fun `does not register light sensor if is not configured`() {
+ controller.restart()
+
+ verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
+ }
+
+ fun `does not register light sensor if missing`() {
+ controller.configure(dummySensorData, DISPLAY_ID)
+ controller.restart()
+
+ verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
+ }
+
+ fun `register light sensor if configured and present`() {
+ testInjector.lightSensor = TestUtils
+ .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
+ controller.configure(dummySensorData, DISPLAY_ID)
+ controller.restart()
+
+ verify(mockSensorManager).registerListener(any(),
+ testInjector.lightSensor, LIGHT_SENSOR_RATE * 1000, mockHandler)
+ verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
+ }
+
+ fun `register light sensor once if not changed`() {
+ testInjector.lightSensor = TestUtils
+ .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
+ controller.configure(dummySensorData, DISPLAY_ID)
+
+ controller.restart()
+ controller.restart()
+
+ verify(mockSensorManager).registerListener(any(),
+ testInjector.lightSensor, LIGHT_SENSOR_RATE * 1000, mockHandler)
+ verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
+ }
+
+ fun `register new light sensor and unregister old if changed`() {
+ val lightSensor1 = TestUtils
+ .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
+ testInjector.lightSensor = lightSensor1
+ controller.configure(dummySensorData, DISPLAY_ID)
+ controller.restart()
+
+ val lightSensor2 = TestUtils
+ .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
+ testInjector.lightSensor = lightSensor2
+ controller.configure(dummySensorData, DISPLAY_ID)
+ controller.restart()
+
+ inOrder {
+ verify(mockSensorManager).registerListener(any(),
+ lightSensor1, LIGHT_SENSOR_RATE * 1000, mockHandler)
+ verify(mockSensorManager).unregisterListener(any<SensorEventListener>())
+ verify(mockAmbientFilter).clear()
+ verify(mockLightSensorListener).onAmbientLuxChange(LightSensorController.INVALID_LUX)
+ verify(mockSensorManager).registerListener(any(),
+ lightSensor2, LIGHT_SENSOR_RATE * 1000, mockHandler)
+ }
+ verifyNoMoreInteractions(mockSensorManager, mockAmbientFilter, mockLightSensorListener)
+ }
+
+ fun `notifies listener on ambient lux change`() {
+ val expectedLux = 40f
+ val eventLux = 50
+ val eventTime = 60L
+ whenever(mockAmbientFilter.getEstimate(NOW)).thenReturn(expectedLux)
+ val listenerCaptor = argumentCaptor<SensorEventListener>()
+ testInjector.lightSensor = TestUtils
+ .createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT)
+ controller.configure(dummySensorData, DISPLAY_ID)
+ controller.restart()
+ verify(mockSensorManager).registerListener(listenerCaptor.capture(),
+ eq(testInjector.lightSensor), eq(LIGHT_SENSOR_RATE * 1000), eq(mockHandler))
+
+ val listener = listenerCaptor.lastValue
+ listener.onSensorChanged(TestUtils.createSensorEvent(testInjector.lightSensor,
+ eventLux, eventTime * 1_000_000))
+
+ inOrder {
+ verify(mockAmbientFilter).addValue(eventTime, eventLux.toFloat())
+ verify(mockLightSensorListener).onAmbientLuxChange(expectedLux)
+ }
+ }
+
+ private inner class TestInjector : Injector() {
+ var lightSensor: Sensor? = null
+ override fun getLightSensor(sensorManager: SensorManager?,
+ sensorData: SensorData?, fallbackType: Int): Sensor? {
+ return lightSensor
+ }
+
+ override fun getLightSensorRate(resources: Resources?): Int {
+ return LIGHT_SENSOR_RATE
+ }
+
+ override fun getAmbientFilter(resources: Resources?): AmbientFilter {
+ return mockAmbientFilter
+ }
+
+ override fun getTime(): Long {
+ return NOW
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
index 498bffd..4e10b98 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
@@ -192,7 +192,7 @@
}
@Test
- public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() {
+ public void testAutoBrightnessState_DeviceIsInDoze_ConfigDoesAllow() {
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
int targetDisplayState = Display.STATE_DOZE;
boolean allowAutoBrightnessWhileDozing = true;
@@ -218,6 +218,32 @@
}
@Test
+ public void testAutoBrightnessState_DeviceIsInDoze_ConfigDoesAllow_ScreenOff() {
+ mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ int targetDisplayState = Display.STATE_OFF;
+ boolean allowAutoBrightnessWhileDozing = true;
+ int brightnessReason = BrightnessReason.REASON_UNKNOWN;
+ int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ float lastUserSetBrightness = 0.2f;
+ boolean userSetBrightnessChanged = true;
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
+ mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController)
+ .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
+ mBrightnessConfiguration,
+ lastUserSetBrightness,
+ userSetBrightnessChanged, /* adjustment */ 0.4f,
+ /* userChangedAutoBrightnessAdjustment= */ true, policy,
+ targetDisplayState, /* shouldResetShortTermModel */ true);
+ assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
+ assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
+ }
+
+ @Test
public void testAutoBrightnessState_DisplayIsOn() {
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
int targetDisplayState = Display.STATE_ON;
@@ -245,6 +271,33 @@
}
@Test
+ public void testAutoBrightnessState_DisplayIsOn_PolicyIsDoze() {
+ mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ int targetDisplayState = Display.STATE_ON;
+ boolean allowAutoBrightnessWhileDozing = false;
+ int brightnessReason = BrightnessReason.REASON_UNKNOWN;
+ float lastUserSetBrightness = 0.2f;
+ boolean userSetBrightnessChanged = true;
+ int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ float pendingBrightnessAdjustment = 0.1f;
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
+ mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController)
+ .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
+ mBrightnessConfiguration,
+ lastUserSetBrightness,
+ userSetBrightnessChanged, pendingBrightnessAdjustment,
+ /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ /* shouldResetShortTermModel */ true);
+ assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
+ assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
+ }
+
+ @Test
public void accommodateUserBrightnessChangesWorksAsExpected() {
// Verify the state if automaticBrightnessController is configured.
assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 1d04baa..e16377e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -202,7 +202,7 @@
}
@Test
- public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() {
+ public void testAutoBrightnessState_DeviceIsInDoze_ConfigDoesAllow() {
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
int targetDisplayState = Display.STATE_DOZE;
boolean allowAutoBrightnessWhileDozing = true;
@@ -228,6 +228,32 @@
}
@Test
+ public void testAutoBrightnessState_DeviceIsInDoze_ConfigDoesAllow_ScreenOff() {
+ mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ int targetDisplayState = Display.STATE_OFF;
+ boolean allowAutoBrightnessWhileDozing = true;
+ int brightnessReason = BrightnessReason.REASON_UNKNOWN;
+ int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ float lastUserSetBrightness = 0.2f;
+ boolean userSetBrightnessChanged = true;
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
+ mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController)
+ .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
+ mBrightnessConfiguration,
+ lastUserSetBrightness,
+ userSetBrightnessChanged, /* adjustment */ 0.4f,
+ /* userChangedAutoBrightnessAdjustment= */ true, policy,
+ targetDisplayState, /* shouldResetShortTermModel */ true);
+ assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
+ assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
+ }
+
+ @Test
public void testAutoBrightnessState_DisplayIsOn() {
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
int targetDisplayState = Display.STATE_ON;
@@ -255,6 +281,33 @@
}
@Test
+ public void testAutoBrightnessState_DisplayIsOn_PolicyIsDoze() {
+ mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ int targetDisplayState = Display.STATE_ON;
+ boolean allowAutoBrightnessWhileDozing = false;
+ int brightnessReason = BrightnessReason.REASON_UNKNOWN;
+ float lastUserSetBrightness = 0.2f;
+ boolean userSetBrightnessChanged = true;
+ int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+ float pendingBrightnessAdjustment = 0.1f;
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
+ mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController)
+ .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
+ mBrightnessConfiguration,
+ lastUserSetBrightness,
+ userSetBrightnessChanged, pendingBrightnessAdjustment,
+ /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+ /* shouldResetShortTermModel */ true);
+ assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
+ assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
+ }
+
+ @Test
public void testAutoBrightnessState_modeSwitch() {
// Setup the test
when(mDisplayManagerFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
diff --git a/services/tests/dreamservicetests/AndroidManifest.xml b/services/tests/dreamservicetests/AndroidManifest.xml
index 449521b..6092ef6 100644
--- a/services/tests/dreamservicetests/AndroidManifest.xml
+++ b/services/tests/dreamservicetests/AndroidManifest.xml
@@ -53,35 +53,6 @@
android:name="android.service.dream"
android:resource="@xml/test_dream_metadata_invalid" />
</service>
-
- <service
- android:name="com.android.server.dreams.TestDreamServiceWithNonexistentSettings"
- android:exported="false"
- android:label="Test Dream" >
- <intent-filter>
- <action android:name="android.service.dreams.DreamService" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- <meta-data
- android:name="android.service.dream"
- android:resource="@xml/test_dream_metadata_nonexistent_settings" />
- </service>
-
- <service
- android:name="com.android.server.dreams.TestDreamServiceNoPackageNonexistentSettings"
- android:exported="false"
- android:label="Test Dream" >
- <intent-filter>
- <action android:name="android.service.dreams.DreamService" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- <meta-data
- android:name="android.service.dream"
- android:resource="@xml/test_dream_metadata_nopackage_nonexistent_settings" />
- </service>
-
- <activity android:name=".TestDreamSettingsActivity">
- </activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/services/tests/dreamservicetests/res/xml/test_dream_metadata_nopackage_nonexistent_settings.xml b/services/tests/dreamservicetests/res/xml/test_dream_metadata_nopackage_nonexistent_settings.xml
deleted file mode 100644
index 0453e92..0000000
--- a/services/tests/dreamservicetests/res/xml/test_dream_metadata_nopackage_nonexistent_settings.xml
+++ /dev/null
@@ -1,20 +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.
- -->
-
-<!-- The settings activity does not exist, which is invalid. -->
-<dream xmlns:android="http://schemas.android.com/apk/res/android"
- android:settingsActivity="com.android.frameworks.dreamservicetests.TestDreamSettingsActivity2"
- android:showClockAndComplications="false"/>
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
index b4e1abf..b98af6b 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
@@ -91,28 +91,6 @@
}
@Test
- public void testMetadataParsing_nonexistentSettingsActivity()
- throws PackageManager.NameNotFoundException {
- final String testDreamClassName =
- "com.android.server.dreams.TestDreamServiceWithNonexistentSettings";
- final DreamService.DreamMetadata metadata = getDreamMetadata(testDreamClassName);
-
- assertThat(metadata.settingsActivity).isNull();
- assertThat(metadata.dreamCategory).isEqualTo(DreamService.DREAM_CATEGORY_DEFAULT);
- }
-
- @Test
- public void testMetadataParsing_noPackage_nonexistentSettingsActivity()
- throws PackageManager.NameNotFoundException {
- final String testDreamClassName =
- "com.android.server.dreams.TestDreamServiceNoPackageNonexistentSettings";
- final DreamService.DreamMetadata metadata = getDreamMetadata(testDreamClassName);
-
- assertThat(metadata.settingsActivity).isNull();
- assertThat(metadata.dreamCategory).isEqualTo(DreamService.DREAM_CATEGORY_DEFAULT);
- }
-
- @Test
public void testMetadataParsing_exceptionReading() {
final PackageManager packageManager = Mockito.mock(PackageManager.class);
final ServiceInfo serviceInfo = Mockito.mock(ServiceInfo.class);
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
index 3d03bf2..e2b93ae 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
@@ -205,7 +205,7 @@
@Override
public DreamOverlayConnectionHandler createOverlayConnection(
- ComponentName overlayComponent) {
+ ComponentName overlayComponent, Runnable onDisconnected) {
return mDreamOverlayConnectionHandler;
}
diff --git a/services/tests/mockingservicestests/src/android/service/dreams/DreamOverlayConnectionHandlerTest.java b/services/tests/mockingservicestests/src/android/service/dreams/DreamOverlayConnectionHandlerTest.java
index 22d7e73..3e65585 100644
--- a/services/tests/mockingservicestests/src/android/service/dreams/DreamOverlayConnectionHandlerTest.java
+++ b/services/tests/mockingservicestests/src/android/service/dreams/DreamOverlayConnectionHandlerTest.java
@@ -49,10 +49,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayConnectionHandlerTest {
- private static final int MIN_CONNECTION_DURATION_MS = 100;
- private static final int MAX_RECONNECT_ATTEMPTS = 3;
- private static final int BASE_RECONNECT_DELAY_MS = 50;
-
@Mock
private Context mContext;
@Mock
@@ -63,6 +59,8 @@
private IDreamOverlay mOverlayService;
@Mock
private IDreamOverlayClient mOverlayClient;
+ @Mock
+ private Runnable mOnDisconnectRunnable;
private TestLooper mTestLooper;
private DreamOverlayConnectionHandler mDreamOverlayConnectionHandler;
@@ -75,9 +73,7 @@
mContext,
mTestLooper.getLooper(),
mServiceIntent,
- MIN_CONNECTION_DURATION_MS,
- MAX_RECONNECT_ATTEMPTS,
- BASE_RECONNECT_DELAY_MS,
+ mOnDisconnectRunnable,
new TestInjector(mConnection));
}
@@ -119,12 +115,14 @@
mTestLooper.dispatchAll();
// No client yet, so we shouldn't have executed
verify(consumer, never()).accept(mOverlayClient);
+ verify(mOnDisconnectRunnable, never()).run();
provideClient();
// Service disconnected before looper could handle the message.
disconnectService();
mTestLooper.dispatchAll();
verify(consumer, never()).accept(mOverlayClient);
+ verify(mOnDisconnectRunnable).run();
}
@Test
@@ -237,8 +235,7 @@
@Override
public PersistentServiceConnection<IDreamOverlay> buildConnection(Context context,
- Handler handler, Intent serviceIntent, int minConnectionDurationMs,
- int maxReconnectAttempts, int baseReconnectDelayMs) {
+ Handler handler, Intent serviceIntent) {
return mConnection;
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 42814e7..3377899 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -74,7 +74,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -227,9 +226,6 @@
setCrashRecoveryPropRescueBootCount(0);
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
-
- // enable flag resets for tests
- mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_RESCUE_PARTY_FLAG_RESETS);
}
@After
@@ -250,9 +246,6 @@
noteBoot(1);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
- verifiedTimesMap);
-
// Record DeviceConfig accesses
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
@@ -262,15 +255,8 @@
final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
noteBoot(2);
-
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces,
- verifiedTimesMap);
-
noteBoot(3);
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
- verifiedTimesMap);
-
noteBoot(4);
assertTrue(RescueParty.isRebootPropertySet());
@@ -296,7 +282,6 @@
final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
noteBoot(1);
- verifyDeviceConfigReset(expectedAllResetNamespaces, verifiedTimesMap);
noteBoot(2);
assertTrue(RescueParty.isRebootPropertySet());
@@ -321,20 +306,9 @@
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
noteAppCrash(1, true);
-
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
- /*configResetVerifiedTimesMap=*/ null);
-
noteAppCrash(2, true);
-
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
- /*configResetVerifiedTimesMap=*/ null);
-
noteAppCrash(3, true);
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
- /*configResetVerifiedTimesMap=*/ null);
-
noteAppCrash(4, true);
assertTrue(RescueParty.isRebootPropertySet());
@@ -362,10 +336,8 @@
final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
noteAppCrash(1, true);
- verifyDeviceConfigReset(expectedResetNamespaces, verifiedTimesMap);
noteAppCrash(2, true);
- verifyDeviceConfigReset(expectedAllResetNamespaces, verifiedTimesMap);
noteAppCrash(3, true);
assertTrue(RescueParty.isRebootPropertySet());
@@ -385,26 +357,13 @@
}
@Test
- public void testNonPersistentAppOnlyPerformsFlagResets() {
+ public void testNonPersistentAppDoesntDoAnything() {
// this is old test where the flag needs to be disabled
mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
noteAppCrash(1, false);
-
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
- /*configResetVerifiedTimesMap=*/ null);
-
noteAppCrash(2, false);
-
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
- /*configResetVerifiedTimesMap=*/ null);
-
noteAppCrash(3, false);
-
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
- /*configResetVerifiedTimesMap=*/ null);
-
- noteAppCrash(4, false);
assertFalse(RescueParty.isRebootPropertySet());
noteAppCrash(5, false);
@@ -430,10 +389,8 @@
final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
noteAppCrash(1, false);
- verifyDeviceConfigReset(expectedResetNamespaces, verifiedTimesMap);
noteAppCrash(2, false);
- verifyDeviceConfigReset(expectedAllResetNamespaces, verifiedTimesMap);
noteAppCrash(3, false);
assertFalse(RescueParty.isRebootPropertySet());
@@ -488,87 +445,12 @@
HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces,
- verifiedTimesMap);
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedResetNamespaces,
- verifiedTimesMap);
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3);
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
- verifiedTimesMap);
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
- assertFalse(RescueParty.isRebootPropertySet());
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
- assertFalse(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
- public void testNonDeviceConfigSettingsOnlyResetOncePerLevel() {
- // this is old test where the flag needs to be disabled
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
-
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
-
- // Record DeviceConfig accesses
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
- // Fake DeviceConfig value changes
- monitorCallback.onNamespaceUpdate(NAMESPACE1);
- monitorCallback.onNamespaceUpdate(NAMESPACE2);
- monitorCallback.onNamespaceUpdate(NAMESPACE3);
- // Perform and verify scoped resets
- final String[] expectedPackage1ResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
- final String[] expectedPackage2ResetNamespaces = new String[]{NAMESPACE2, NAMESPACE3};
- final String[] expectedAllResetNamespaces =
- new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3};
- HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
- expectedPackage1ResetNamespaces, verifiedTimesMap);
-
- // Settings.Global & Settings.Secure should still remain the same execution times.
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
- expectedPackage2ResetNamespaces, verifiedTimesMap);
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES,
- expectedPackage1ResetNamespaces, verifiedTimesMap);
-
- // Settings.Global & Settings.Secure should still remain the same execution times.
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES,
- expectedPackage2ResetNamespaces, verifiedTimesMap);
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3);
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
- verifiedTimesMap);
-
- // Settings.Global & Settings.Secure should still remain the same execution times.
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE2, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3);
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces,
- verifiedTimesMap);
observer.execute(new VersionedPackage(
CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
@@ -863,27 +745,27 @@
assertEquals(observer.onHealthCheckFailed(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_20);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
assertEquals(observer.onHealthCheckFailed(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_20);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
assertEquals(observer.onHealthCheckFailed(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_20);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
assertEquals(observer.onHealthCheckFailed(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_20);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
assertEquals(observer.onHealthCheckFailed(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 6),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_20);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
assertEquals(observer.onHealthCheckFailed(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 7),
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_20);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
}
@Test
@@ -906,7 +788,7 @@
mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_20);
+ assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_LEVEL_71);
assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_LEVEL_75);
@@ -1037,7 +919,6 @@
private void verifySettingsResets(int resetMode, String[] resetNamespaces,
HashMap<String, Integer> configResetVerifiedTimesMap) {
verifyOnlySettingsReset(resetMode);
- verifyDeviceConfigReset(resetNamespaces, configResetVerifiedTimesMap);
}
private void verifyOnlySettingsReset(int resetMode) {
@@ -1054,26 +935,6 @@
eq(resetMode), anyInt()), never());
}
- private void verifyDeviceConfigReset(String[] resetNamespaces,
- Map<String, Integer> configResetVerifiedTimesMap) {
- if (resetNamespaces == null) {
- verify(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()), never());
- } else {
- for (String namespace : resetNamespaces) {
- int verifiedTimes = 0;
- if (configResetVerifiedTimesMap != null
- && configResetVerifiedTimesMap.get(namespace) != null) {
- verifiedTimes = configResetVerifiedTimesMap.get(namespace);
- }
- verify(() -> DeviceConfig.resetToDefaults(RescueParty.DEVICE_CONFIG_RESET_MODE,
- namespace), times(verifiedTimes + 1));
- if (configResetVerifiedTimesMap != null) {
- configResetVerifiedTimesMap.put(namespace, verifiedTimes + 1);
- }
- }
- }
- }
-
private void noteBoot(int mitigationCount) {
RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java
index 75e8e68..72883e2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/UserWakeupStoreTest.java
@@ -134,6 +134,18 @@
}
@Test
+ public void testOnUserStarting_userIsRemovedFromTheStore() {
+ mUserWakeupStore.addUserWakeup(USER_ID_1, TEST_TIMESTAMP - 19_000);
+ mUserWakeupStore.addUserWakeup(USER_ID_2, TEST_TIMESTAMP - 7_000);
+ mUserWakeupStore.addUserWakeup(USER_ID_3, TEST_TIMESTAMP - 13_000);
+ assertEquals(3, mUserWakeupStore.getUserIdsToWakeup(TEST_TIMESTAMP).length);
+ mUserWakeupStore.onUserStarting(USER_ID_3);
+ // getWakeupTimeForUser returns negative wakeup time if there is no entry for user.
+ assertEquals(-1, mUserWakeupStore.getWakeupTimeForUser(USER_ID_3));
+ assertEquals(2, mUserWakeupStore.getUserIdsToWakeup(TEST_TIMESTAMP).length);
+ }
+
+ @Test
public void testGetNextUserWakeup() {
mUserWakeupStore.addUserWakeup(USER_ID_1, TEST_TIMESTAMP - 19_000);
mUserWakeupStore.addUserWakeup(USER_ID_2, TEST_TIMESTAMP - 3_000);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index e15942b..adcbf5c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -84,7 +84,11 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
import java.util.Random;
+import java.util.function.Function;
import java.util.zip.GZIPInputStream;
/**
@@ -940,6 +944,228 @@
}
}
+ private ApplicationExitInfo createExitInfo(int i) {
+ ApplicationExitInfo info = new ApplicationExitInfo();
+ info.setPid(i);
+ info.setTimestamp(1000 + i);
+ info.setPackageUid(2000);
+ return info;
+ }
+
+ @SuppressWarnings("GuardedBy")
+ private ArrayList<ApplicationExitInfo> getExitInfosHelper(
+ AppExitInfoTracker.AppExitInfoContainer container, int filterPid, int maxNum) {
+ ArrayList<ApplicationExitInfo> infos = new ArrayList<ApplicationExitInfo>();
+ container.getExitInfosLocked(filterPid, maxNum, infos);
+ return infos;
+ }
+
+ @SuppressWarnings("GuardedBy")
+ private void checkAreHelper(AppExitInfoTracker.AppExitInfoContainer container, int filterPid,
+ int maxNum, List<Integer> expected, Function<ApplicationExitInfo, Integer> func) {
+ ArrayList<Integer> values = new ArrayList<Integer>();
+ getExitInfosHelper(container, filterPid, maxNum)
+ .forEach((exitInfo) -> values.add(func.apply(exitInfo)));
+ assertEquals(values, expected);
+
+ HashMap<Integer, Integer> expectedMultiset = new HashMap<Integer, Integer>();
+ expected.forEach(
+ (elem) -> expectedMultiset.put(elem, expectedMultiset.getOrDefault(elem, 0) + 1));
+ // `maxNum` isn't a parameter supported by `forEachRecordLocked()s`, but we can emulate it
+ // by stopping iteration when we've seen enough elements.
+ int[] numElementsToObserveWrapped = {maxNum};
+ container.forEachRecordLocked((exitInfo) -> {
+ // Same thing as above, `filterPid` isn't a parameter supported out of the box for
+ // `forEachRecordLocked()`, but we emulate it here.
+ if (filterPid > 0 && filterPid != exitInfo.getPid()) {
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ }
+
+ Integer key = func.apply(exitInfo);
+ assertTrue(expectedMultiset.toString(), expectedMultiset.containsKey(key));
+ Integer references = expectedMultiset.get(key);
+ if (references == 1) {
+ expectedMultiset.remove(key);
+ } else {
+ expectedMultiset.put(key, references - 1);
+ }
+ if (--numElementsToObserveWrapped[0] == 0) {
+ return AppExitInfoTracker.FOREACH_ACTION_STOP_ITERATION;
+ }
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
+ assertEquals(expectedMultiset.size(), 0);
+ }
+
+ private void checkPidsAre(AppExitInfoTracker.AppExitInfoContainer container, int filterPid,
+ int maxNum, List<Integer> expectedPids) {
+ checkAreHelper(container, filterPid, maxNum, expectedPids, (exitInfo) -> exitInfo.getPid());
+ }
+
+ private void checkPidsAre(
+ AppExitInfoTracker.AppExitInfoContainer container, List<Integer> expectedPids) {
+ checkPidsAre(container, 0, 0, expectedPids);
+ }
+
+ private void checkTimestampsAre(AppExitInfoTracker.AppExitInfoContainer container,
+ int filterPid, int maxNum, List<Integer> expectedTimestamps) {
+ checkAreHelper(container, filterPid, maxNum, expectedTimestamps,
+ (exitInfo) -> (int) exitInfo.getTimestamp());
+ }
+
+ private void checkTimestampsAre(
+ AppExitInfoTracker.AppExitInfoContainer container, List<Integer> expectedTimestamps) {
+ checkTimestampsAre(container, 0, 0, expectedTimestamps);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ private AppExitInfoTracker.AppExitInfoContainer createBasicContainer() {
+ AppExitInfoTracker.AppExitInfoContainer container =
+ mAppExitInfoTracker.new AppExitInfoContainer(3);
+ container.addExitInfoLocked(createExitInfo(10));
+ container.addExitInfoLocked(createExitInfo(30));
+ container.addExitInfoLocked(createExitInfo(20));
+ return container;
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerGetExitInfosIsSortedNewestFirst() throws Exception {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ checkPidsAre(container, Arrays.asList(30, 20, 10));
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerRemovesOldestReports() throws Exception {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ container.addExitInfoLocked(createExitInfo(40));
+ checkPidsAre(container, Arrays.asList(40, 30, 20));
+
+ container.addExitInfoLocked(createExitInfo(50));
+ checkPidsAre(container, Arrays.asList(50, 40, 30));
+
+ container.addExitInfoLocked(createExitInfo(45));
+ checkPidsAre(container, Arrays.asList(50, 45, 40));
+
+ // Adding an older report shouldn't remove the newer ones.
+ container.addExitInfoLocked(createExitInfo(15));
+ checkPidsAre(container, Arrays.asList(50, 45, 40));
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerFilterByPid() throws Exception {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ assertEquals(1, getExitInfosHelper(container, 30, 0).size());
+ assertEquals(30, getExitInfosHelper(container, 0, 0).get(0).getPid());
+
+ assertEquals(1, getExitInfosHelper(container, 30, 0).size());
+ assertEquals(20, getExitInfosHelper(container, 20, 0).get(0).getPid());
+
+ assertEquals(1, getExitInfosHelper(container, 10, 0).size());
+ assertEquals(10, getExitInfosHelper(container, 10, 0).get(0).getPid());
+
+ assertEquals(0, getExitInfosHelper(container, 1337, 0).size());
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerLimitQuantityOfResults() throws Exception {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ checkPidsAre(container, /* filterPid */ 30, /* maxNum */ 1, Arrays.asList(30));
+ checkPidsAre(container, /* filterPid */ 30, /* maxNum */ 1000, Arrays.asList(30));
+
+ checkPidsAre(container, /* filterPid */ 20, /* maxNum */ 1, Arrays.asList(20));
+ checkPidsAre(container, /* filterPid */ 20, /* maxNum */ 1000, Arrays.asList(20));
+
+ checkPidsAre(container, /* filterPid */ 10, /* maxNum */ 1, Arrays.asList(10));
+ checkPidsAre(container, /* filterPid */ 10, /* maxNum */ 1000, Arrays.asList(10));
+
+ checkPidsAre(container, /* filterPid */ 1337, /* maxNum */ 1, Arrays.asList());
+ checkPidsAre(container, /* filterPid */ 1337, /* maxNum */ 1000, Arrays.asList());
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerLastExitInfoForPid() throws Exception {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ assertEquals(30, container.getLastExitInfoForPid(30).getPid());
+ assertEquals(20, container.getLastExitInfoForPid(20).getPid());
+ assertEquals(10, container.getLastExitInfoForPid(10).getPid());
+ assertEquals(null, container.getLastExitInfoForPid(1337));
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerCanHoldMultipleFromSamePid() throws Exception {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ ApplicationExitInfo info = createExitInfo(100);
+ ApplicationExitInfo info2 = createExitInfo(100);
+ ApplicationExitInfo info3 = createExitInfo(100);
+ info2.setTimestamp(1337);
+ info3.setTimestamp(31337);
+
+ container.addExitInfoLocked(info);
+ assertEquals(1100, container.getLastExitInfoForPid(100).getTimestamp());
+ container.addExitInfoLocked(info2);
+ assertEquals(1337, container.getLastExitInfoForPid(100).getTimestamp());
+ container.addExitInfoLocked(info3);
+ assertEquals(31337, container.getLastExitInfoForPid(100).getTimestamp());
+
+ checkPidsAre(container, Arrays.asList(100, 100, 100));
+ checkTimestampsAre(container, Arrays.asList(31337, 1337, 1100));
+
+ checkPidsAre(container, /* filterPid */ 100, /* maxNum */ 0, Arrays.asList(100, 100, 100));
+ checkTimestampsAre(
+ container, /* filterPid */ 100, /* maxNum */ 0, Arrays.asList(31337, 1337, 1100));
+
+ checkPidsAre(container, /* filterPid */ 100, /* maxNum */ 2, Arrays.asList(100, 100));
+ checkTimestampsAre(
+ container, /* filterPid */ 100, /* maxNum */ 2, Arrays.asList(31337, 1337));
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerIteration() throws Exception {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ checkPidsAre(container, Arrays.asList(30, 20, 10));
+
+ // Unfortunately relying on order for this test, which is implemented as "last inserted" ->
+ // "first inserted". Note that this is insertion order, not timestamp. Thus, it's 20 -> 30
+ // -> 10, as defined by `createBasicContainer()`.
+ List<Integer> elements = Arrays.asList(20, 30, 10);
+ for (int i = 0, size = elements.size(); i < size; i++) {
+ ArrayList<Integer> processedEntries = new ArrayList<Integer>();
+ final int finalIndex = i;
+ container.forEachRecordLocked((exitInfo) -> {
+ processedEntries.add(new Integer(exitInfo.getPid()));
+ if (exitInfo.getPid() == elements.get(finalIndex)) {
+ return AppExitInfoTracker.FOREACH_ACTION_STOP_ITERATION;
+ }
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
+ assertEquals(processedEntries, elements.subList(0, i + 1));
+ }
+ }
+
+ @Test
+ @SuppressWarnings("GuardedBy")
+ public void testContainerIterationRemove() throws Exception {
+ for (int pidToRemove : Arrays.asList(30, 20, 10)) {
+ AppExitInfoTracker.AppExitInfoContainer container = createBasicContainer();
+ container.forEachRecordLocked((exitInfo) -> {
+ if (exitInfo.getPid() == pidToRemove) {
+ return AppExitInfoTracker.FOREACH_ACTION_REMOVE_ITEM;
+ }
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
+ ArrayList<Integer> pidsRemaining = new ArrayList<Integer>(Arrays.asList(30, 20, 10));
+ pidsRemaining.remove(new Integer(pidToRemove));
+ checkPidsAre(container, pidsRemaining);
+ }
+ }
+
private static int makeExitStatus(int exitCode) {
return (exitCode << 8) & 0xff00;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 488ce66..6366f24 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -72,14 +72,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.AdditionalAnswers.answer;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -486,27 +484,22 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasActivities();
- doAnswer(answer(callback -> {
- Field field = callback.getClass().getDeclaredField("adj");
- field.set(callback, VISIBLE_APP_ADJ);
- field = callback.getClass().getDeclaredField("foregroundActivities");
- field.set(callback, true);
- field = callback.getClass().getDeclaredField("procState");
- field.set(callback, PROCESS_STATE_TOP);
- field = callback.getClass().getDeclaredField("schedGroup");
- field.set(callback, SCHED_GROUP_TOP_APP);
- field = callback.getClass().getDeclaredField("mAdjType");
- field.set(callback, "vis-activity");
- return 0;
- })).when(wpc).computeOomAdjFromActivities(
- any(WindowProcessController.ComputeOomAdjCallback.class));
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+ .when(wpc).getActivityStateFlags();
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT);
assertFalse(app.mState.isCached());
assertFalse(app.mState.isEmpty());
assertEquals("vis-activity", app.mState.getAdjType());
+
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN)
+ .when(wpc).getActivityStateFlags();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
+ assertEquals("resumed-split-screen-activity", app.mState.getAdjType());
}
@SuppressWarnings("GuardedBy")
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
deleted file mode 100644
index c5e6824..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssLocationProviderTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.location.gnss;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.AlarmManager;
-import android.app.AppOpsManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.location.GnssCapabilities;
-import android.location.LocationManager;
-import android.location.LocationManagerInternal;
-import android.location.flags.Flags;
-import android.location.provider.ProviderRequest;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
-import com.android.server.location.gnss.hal.FakeGnssHal;
-import com.android.server.location.gnss.hal.GnssNative;
-import com.android.server.location.injector.Injector;
-import com.android.server.location.injector.TestInjector;
-import com.android.server.timedetector.TimeDetectorInternal;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.quality.Strictness;
-
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-@Presubmit
-@androidx.test.filters.SmallTest
-@RunWith(AndroidJUnit4.class)
-public class GnssLocationProviderTest {
-
- @Rule
- public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
- .setStrictness(Strictness.WARN)
- .mockStatic(Settings.Global.class)
- .build();
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
- private @Mock Context mContext;
- private @Mock LocationManagerInternal mLocationManagerInternal;
- private @Mock LocationManager mLocationManager;
- private @Mock TimeDetectorInternal mTimeDetectorInternal;
- private @Mock GnssConfiguration mMockConfiguration;
- private @Mock GnssMetrics mGnssMetrics;
- private @Mock PowerManager mPowerManager;
- private @Mock TelephonyManager mTelephonyManager;
- private @Mock AppOpsManager mAppOpsManager;
- private @Mock AlarmManager mAlarmManager;
- private @Mock PowerManager.WakeLock mWakeLock;
- private @Mock ContentResolver mContentResolver;
- private @Mock UserManager mUserManager;
- private @Mock UserHandle mUserHandle;
- private Set<UserHandle> mUserHandleSet = new HashSet<>();
-
- private GnssNative mGnssNative;
-
- private GnssLocationProvider mTestProvider;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- doReturn("mypackage").when(mContext).getPackageName();
- doReturn("attribution").when(mContext).getAttributionTag();
- doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
- doReturn(mPowerManager).when(mContext).getSystemService("power");
- doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
- doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
- doReturn(mAlarmManager).when(mContext).getSystemService(Context.ALARM_SERVICE);
- doReturn(mLocationManager).when(mContext).getSystemService(LocationManager.class);
- doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
- mUserHandleSet.add(mUserHandle);
- doReturn(true).when(mLocationManager).isLocationEnabledForUser(eq(mUserHandle));
- doReturn(mUserHandleSet).when(mUserManager).getVisibleUsers();
- doReturn(mContentResolver).when(mContext).getContentResolver();
- doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
- LocalServices.addService(TimeDetectorInternal.class, mTimeDetectorInternal);
- FakeGnssHal fakeGnssHal = new FakeGnssHal();
- GnssNative.setGnssHalForTest(fakeGnssHal);
- Injector injector = new TestInjector(mContext);
- mGnssNative = spy(Objects.requireNonNull(
- GnssNative.create(injector, mMockConfiguration)));
- doReturn(true).when(mGnssNative).init();
- GnssCapabilities gnssCapabilities = new GnssCapabilities.Builder().setHasScheduling(
- true).build();
- doReturn(gnssCapabilities).when(mGnssNative).getCapabilities();
-
- mTestProvider = new GnssLocationProvider(mContext, mGnssNative, mGnssMetrics);
- mGnssNative.register();
- }
-
- @After
- public void tearDown() {
- LocalServices.removeServiceForTest(LocationManagerInternal.class);
- LocalServices.removeServiceForTest(TimeDetectorInternal.class);
- }
-
- @Test
- public void testStartNavigating() {
- ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
- 0).build();
-
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).start();
- }
-
- @Test
- public void testUpdateRequirements_sameRequest() {
- mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
- ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
- 0).build();
-
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).start();
-
- // set the same request
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative, never()).stop();
- verify(mGnssNative, times(1)).start();
- }
-
- @Test
- public void testUpdateRequirements_differentRequest() {
- mSetFlagsRule.enableFlags(Flags.FLAG_GNSS_CALL_STOP_BEFORE_SET_POSITION_MODE);
- ProviderRequest providerRequest = new ProviderRequest.Builder().setIntervalMillis(
- 0).build();
-
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).start();
-
- // set a different request
- providerRequest = new ProviderRequest.Builder().setIntervalMillis(2000).build();
- mTestProvider.onSetRequest(providerRequest);
- verify(mGnssNative).stop();
- verify(mGnssNative, times(2)).start();
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
new file mode 100644
index 0000000..a82658b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.media.AudioAttributes;
+import android.media.AudioFocusInfo;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArraySet;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+
+public class BackgroundUserSoundNotifierTest {
+ private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+ private Context mSpiedContext;
+ private BackgroundUserSoundNotifier mBackgroundUserSoundNotifier;
+
+ private UserManager mUserManager;
+ private ArraySet<Integer> mUsersToRemove;
+
+ @Mock
+ private NotificationManager mNotificationManager;
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mSpiedContext = spy(mRealContext);
+ mUsersToRemove = new ArraySet<>();
+ mUserManager = UserManager.get(mRealContext);
+ doReturn(mNotificationManager)
+ .when(mSpiedContext).getSystemService(NotificationManager.class);
+ mBackgroundUserSoundNotifier = new BackgroundUserSoundNotifier(mSpiedContext);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mUsersToRemove.stream().toList().forEach(this::removeUser);
+ }
+ @Test
+ public void testAlarmOnBackgroundUser_ForegroundUserNotified() throws RemoteException {
+ AudioAttributes aa = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM).build();
+ UserInfo user = createUser("User",
+ UserManager.USER_TYPE_FULL_SECONDARY,
+ 0);
+ final int fgUserId = mSpiedContext.getUserId();
+ final int bgUserUid = user.id * 100000;
+ doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser();
+ AudioFocusInfo afi = new AudioFocusInfo(aa, bgUserUid, "",
+ /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
+ AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
+ clearInvocations(mNotificationManager);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ verify(mNotificationManager)
+ .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
+ eq(afi.getClientUid()), any(Notification.class),
+ eq(UserHandle.of(fgUserId)));
+ }
+
+ @Test
+ public void testMediaOnBackgroundUser_ForegroundUserNotNotified() throws RemoteException {
+ AudioAttributes aa = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA).build();
+ UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
+ final int bgUserUid = mSpiedContext.getUserId() * 100000;
+ AudioFocusInfo afi = new AudioFocusInfo(aa, bgUserUid, "",
+ /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
+ AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
+ clearInvocations(mNotificationManager);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ verifyZeroInteractions(mNotificationManager);
+ }
+
+ @Test
+ public void testAlarmOnForegroundUser_ForegroundUserNotNotified() throws RemoteException {
+ AudioAttributes aa = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM).build();
+ final int fgUserId = mSpiedContext.getUserId();
+ final int fgUserUid = fgUserId * 100000;
+ doReturn(UserHandle.of(fgUserId)).when(mSpiedContext).getUser();
+ AudioFocusInfo afi = new AudioFocusInfo(aa, fgUserUid, "", /* packageName= */ "",
+ AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0,
+ Build.VERSION.SDK_INT);
+ clearInvocations(mNotificationManager);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ verifyZeroInteractions(mNotificationManager);
+ }
+
+
+ private UserInfo createUser(String name, String userType, int flags) {
+ UserInfo user = mUserManager.createUser(name, userType, flags);
+ if (user != null) {
+ mUsersToRemove.add(user.id);
+ }
+ return user;
+ }
+ private void removeUser(int userId) {
+ mUserManager.removeUser(userId);
+ }
+
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
index 2a67029..7aec42b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -72,9 +72,6 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.security.KeyStoreAuthorization;
import android.service.trust.GrantTrustResult;
@@ -124,9 +121,6 @@
.build();
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
public final MockContext mMockContext = new MockContext(
ApplicationProvider.getApplicationContext());
@@ -418,7 +412,6 @@
// user, not the profile. This matches the authentication that is needed to unlock the device
// for the profile again.
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testLockDeviceForManagedProfileWithUnifiedChallenge_usesParentBiometricSids()
throws Exception {
setupMocksForProfile(/* unifiedChallenge= */ true);
@@ -617,7 +610,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockEnabled_whenWeakFingerprintIsSetupAndAllowed()
throws Exception {
setupStrongAuthTrackerToAllowEverything();
@@ -626,7 +618,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockEnabled_whenWeakFaceIsSetupAndAllowed() throws Exception {
setupStrongAuthTrackerToAllowEverything();
setupFace(SensorProperties.STRENGTH_WEAK);
@@ -634,7 +625,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockEnabled_whenConvenienceFingerprintIsSetupAndAllowed()
throws Exception {
setupStrongAuthTrackerToAllowEverything();
@@ -643,7 +633,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockEnabled_whenConvenienceFaceIsSetupAndAllowed()
throws Exception {
setupStrongAuthTrackerToAllowEverything();
@@ -652,7 +641,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenStrongAuthRequired() throws Exception {
setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, true);
setupFace(SensorProperties.STRENGTH_WEAK);
@@ -660,7 +648,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenNonStrongBiometricNotAllowed() throws Exception {
setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED,
/* isNonStrongBiometricAllowed= */ false);
@@ -669,7 +656,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenWeakFingerprintSensorIsPresentButNotEnrolled()
throws Exception {
setupStrongAuthTrackerToAllowEverything();
@@ -678,7 +664,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenWeakFaceSensorIsPresentButNotEnrolled()
throws Exception {
setupStrongAuthTrackerToAllowEverything();
@@ -687,7 +672,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void
testKeystoreWeakUnlockDisabled_whenWeakFingerprintIsSetupButForbiddenByDevicePolicy()
throws Exception {
@@ -699,7 +683,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenWeakFaceIsSetupButForbiddenByDevicePolicy()
throws Exception {
setupStrongAuthTrackerToAllowEverything();
@@ -710,7 +693,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFingerprintIsSetup() throws Exception {
setupStrongAuthTrackerToAllowEverything();
setupFingerprint(SensorProperties.STRENGTH_STRONG);
@@ -718,7 +700,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFaceIsSetup() throws Exception {
setupStrongAuthTrackerToAllowEverything();
setupFace(SensorProperties.STRENGTH_STRONG);
@@ -726,7 +707,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
public void testKeystoreWeakUnlockDisabled_whenNoBiometricsAreSetup() throws Exception {
setupStrongAuthTrackerToAllowEverything();
verifyWeakUnlockDisabled();
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 4460c6a..ce2bb95 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.SensorManager;
@@ -41,7 +42,6 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.test.TestLooper;
@@ -82,8 +82,12 @@
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
@Mock private WakeLockLog mWakeLockLog;
+ @Mock private IBatteryStats mBatteryStats;
+
@Mock private PowerManagerFlags mPowerManagerFlags;
+ @Mock private AppOpsManager mAppOpsManager;
+
private PowerManagerService mService;
private Context mContextSpy;
private Resources mResourcesSpy;
@@ -230,7 +234,7 @@
public void testOnWakeLockListener_RemoteException_NoRethrow() {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
createNotifier();
-
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
@Override public void onStateChanged(boolean enabled) throws RemoteException {
throw new RemoteException("Just testing");
@@ -245,6 +249,7 @@
verifyZeroInteractions(mWakeLockLog);
mTestLooper.dispatchAll();
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
+
mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
@@ -277,6 +282,115 @@
verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
}
+
+ @Test
+ public void testOnWakeLockListener_FullWakeLock_ProcessesOnHandler() throws RemoteException {
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+ createNotifier();
+
+ IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+ @Override public void onStateChanged(boolean enabled) throws RemoteException {
+ throw new RemoteException("Just testing");
+ }
+ };
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ final int uid = 1234;
+ final int pid = 5678;
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ // No interaction because we expect that to happen in async
+ verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Progressing the looper, and validating all the interactions
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
+ verify(mBatteryStats).noteStopWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL);
+ verify(mAppOpsManager).finishOp(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", null);
+
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Acquire the wakelock
+ mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ // No interaction because we expect that to happen in async
+ verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Progressing the looper, and validating all the interactions
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, 1);
+ verify(mBatteryStats).noteStartWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL, false);
+ verify(mAppOpsManager).startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", false, null, null);
+
+ // Test with improveWakelockLatency flag false, hence the wakelock log will run on the same
+ // thread
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(false);
+
+ mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, -1);
+
+ mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
+ }
+
+ @Test
+ public void testOnWakeLockListener_FullWakeLock_ProcessesInSync() throws RemoteException {
+ createNotifier();
+
+ IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+ @Override public void onStateChanged(boolean enabled) throws RemoteException {
+ throw new RemoteException("Just testing");
+ }
+ };
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ final int uid = 1234;
+ final int pid = 5678;
+
+ // Release the wakelock
+ mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
+ verify(mBatteryStats).noteStopWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL);
+ verify(mAppOpsManager).finishOp(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", null);
+
+ clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+
+ // Acquire the wakelock
+ mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK, -1);
+ verify(mBatteryStats).noteStartWakelock(uid, pid, "wakelockTag", /* historyTag= */ null,
+ BatteryStats.WAKE_TYPE_FULL, false);
+ verify(mAppOpsManager).startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, uid,
+ "my.package.name", false, null, null);
+ }
+
private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
@@ -365,13 +479,17 @@
public WakeLockLog getWakeLockLog(Context context) {
return mWakeLockLog;
}
+
+ @Override
+ public AppOpsManager getAppOpsManager(Context context) {
+ return mAppOpsManager;
+ }
};
mNotifier = new Notifier(
mTestLooper.getLooper(),
mContextSpy,
- IBatteryStats.Stub.asInterface(ServiceManager.getService(
- BatteryStats.SERVICE_NAME)),
+ mBatteryStats,
mInjector.createSuspendBlocker(mService, "testBlocker"),
null,
null,
diff --git a/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java b/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
index 62075b8..ab1b0cc 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
@@ -17,11 +17,10 @@
package com.android.server.power;
import static com.android.server.power.ShutdownThread.DEFAULT_SHUTDOWN_VIBRATE_MS;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,7 +41,6 @@
import java.io.File;
import java.io.FileOutputStream;
-import java.io.IOException;
/**
* Tests for {@link com.android.server.power.ShutdownThread}
@@ -88,6 +86,7 @@
@Mock private VibratorInfo mVibratorInfoMock;
private String mDefaultShutdownVibrationFilePath;
+ private boolean mShutdownVibrationDisabled;
private long mLastSleepDurationMs;
private ShutdownThread mShutdownThread;
@@ -168,6 +167,17 @@
.vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
}
+ @Test
+ public void testVibrationDisabled() throws Exception {
+ setShutdownVibrationFileContent(CLICK_VIB_SERIALIZATION);
+ mShutdownVibrationDisabled = true;
+
+ mShutdownThread.playShutdownVibration(mContextMock);
+
+ verify(mVibratorMock, never())
+ .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
+ }
+
private void assertShutdownVibration(VibrationEffect effect, long vibrationSleepDuration)
throws Exception {
verify(mVibratorMock).vibrate(
@@ -214,5 +224,10 @@
public void sleep(long durationMs) {
mLastSleepDurationMs = durationMs;
}
+
+ @Override
+ public boolean isShutdownVibrationDisabled(Context context) {
+ return mShutdownVibrationDisabled;
+ }
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
index 9975190..a4688cc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
@@ -42,6 +42,7 @@
@SmallTest
public class AggregatedPowerStatsTest {
private static final int TEST_POWER_COMPONENT = 1077;
+ private static final int CUSTOM_POWER_COMPONENT = 1042;
private static final int APP_1 = 27;
private static final int APP_2 = 42;
private static final int COMPONENT_STATE_0 = 0;
@@ -63,6 +64,20 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+ mAggregatedPowerStatsConfig.trackCustomPowerComponents(
+ () -> new PowerStatsProcessor() {
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats,
+ long timestampMs) {
+ }
+ })
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
SparseArray<String> stateLabels = new SparseArray<>();
stateLabels.put(COMPONENT_STATE_1, "one");
mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2,
@@ -137,6 +152,13 @@
ps.uidStats.put(APP_2, new long[]{100, 200, 300});
stats.addPowerStats(ps, 5000);
+
+ PowerStats custom = new PowerStats(
+ new PowerStats.Descriptor(CUSTOM_POWER_COMPONENT, "cu570m", 1, null, 0, 2,
+ new PersistableBundle()));
+ custom.stats = new long[]{123};
+ custom.uidStats.put(APP_1, new long[]{500, 600});
+ stats.addPowerStats(custom, 6000);
return stats;
}
@@ -149,12 +171,12 @@
assertThat(descriptor.uidStatsArrayLength).isEqualTo(3);
assertThat(descriptor.extras.getString("speed")).isEqualTo("fast");
- assertThat(getDeviceStats(stats,
+ assertThat(getDeviceStats(stats, TEST_POWER_COMPONENT,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_ON))
.isEqualTo(new long[]{322, 987});
- assertThat(getDeviceStats(stats,
+ assertThat(getDeviceStats(stats, TEST_POWER_COMPONENT,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
.isEqualTo(new long[]{222, 0});
@@ -185,51 +207,79 @@
.isEqualTo(new long[]{4500});
assertThat(getUidDeviceStats(stats,
- APP_1,
+ TEST_POWER_COMPONENT, APP_1,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_ON,
BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
.isEqualTo(new long[]{259, 0, 492});
assertThat(getUidDeviceStats(stats,
- APP_1,
+ TEST_POWER_COMPONENT, APP_1,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_ON,
BatteryConsumer.PROCESS_STATE_CACHED))
.isEqualTo(new long[]{129, 0, 446});
assertThat(getUidDeviceStats(stats,
- APP_1,
+ TEST_POWER_COMPONENT, APP_1,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
BatteryConsumer.PROCESS_STATE_CACHED))
.isEqualTo(new long[]{0, 0, 200});
assertThat(getUidDeviceStats(stats,
- APP_2,
+ TEST_POWER_COMPONENT, APP_2,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_ON,
BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
.isEqualTo(new long[]{185, 209, 418});
assertThat(getUidDeviceStats(stats,
- APP_2,
+ TEST_POWER_COMPONENT, APP_2,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_ON,
BatteryConsumer.PROCESS_STATE_FOREGROUND))
.isEqualTo(new long[]{142, 204, 359});
assertThat(getUidDeviceStats(stats,
- APP_2,
+ TEST_POWER_COMPONENT, APP_2,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
BatteryConsumer.PROCESS_STATE_BACKGROUND))
.isEqualTo(new long[]{50, 100, 150});
+
+ descriptor = stats.getPowerComponentStats(CUSTOM_POWER_COMPONENT)
+ .getPowerStatsDescriptor();
+ assertThat(descriptor.powerComponentId).isEqualTo(CUSTOM_POWER_COMPONENT);
+ assertThat(descriptor.statsArrayLength).isEqualTo(1);
+ assertThat(descriptor.uidStatsArrayLength).isEqualTo(2);
+
+ assertThat(getDeviceStats(stats, CUSTOM_POWER_COMPONENT,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{61});
+ assertThat(getDeviceStats(stats, CUSTOM_POWER_COMPONENT,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+ .isEqualTo(new long[]{61});
+ assertThat(getUidDeviceStats(stats,
+ CUSTOM_POWER_COMPONENT, APP_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_CACHED))
+ .isEqualTo(new long[]{250, 300});
+ assertThat(getUidDeviceStats(stats,
+ CUSTOM_POWER_COMPONENT, APP_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+ BatteryConsumer.PROCESS_STATE_CACHED))
+ .isEqualTo(new long[]{250, 300});
}
- private static long[] getDeviceStats(AggregatedPowerStats stats, int... states) {
+ private static long[] getDeviceStats(AggregatedPowerStats stats, int powerComponentId,
+ int... states) {
PowerComponentAggregatedPowerStats powerComponentStats =
- stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ stats.getPowerComponentStats(powerComponentId);
long[] out = new long[powerComponentStats.getPowerStatsDescriptor().statsArrayLength];
powerComponentStats.getDeviceStats(out, states);
return out;
@@ -243,9 +293,10 @@
return out;
}
- private static long[] getUidDeviceStats(AggregatedPowerStats stats, int uid, int... states) {
+ private static long[] getUidDeviceStats(AggregatedPowerStats stats, int powerComponentId,
+ int uid, int... states) {
PowerComponentAggregatedPowerStats powerComponentStats =
- stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ stats.getPowerComponentStats(powerComponentId);
long[] out = new long[powerComponentStats.getPowerStatsDescriptor().uidStatsArrayLength];
powerComponentStats.getUidStats(out, uid, states);
return out;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
index 36deb08..efbd1b7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
@@ -30,8 +30,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
@@ -116,8 +119,10 @@
@Test
public void energyConsumerModel() {
- when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CAMERA, null))
+ when(mConsumedEnergyRetriever
+ .getEnergyConsumerIds(eq((int) EnergyConsumerType.CAMERA), any()))
.thenReturn(new int[]{ENERGY_CONSUMER_ID});
+
CameraPowerStatsProcessor processor = new CameraPowerStatsProcessor(
mStatsRule.getPowerProfile(), mUidResolver);
@@ -131,8 +136,8 @@
collector.setEnabled(true);
// Establish a baseline
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{uCtoUj(10000)});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 10000));
collector.collectAndDeliverStats();
processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
@@ -144,15 +149,15 @@
processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{uCtoUj(2_170_000)});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 2_170_000));
collector.collectAndDeliverStats();
processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
mStatsRule.setTime(11_000, 11_000);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{uCtoUj(3_610_000)});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 3_610_000));
collector.collectAndDeliverStats();
processor.finish(stats, 11_000);
@@ -264,7 +269,10 @@
return powerComponentStats;
}
- private static long uCtoUj(long uc) {
- return (long) (uc * (double) VOLTAGE_MV / 1000);
+ private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUWs) {
+ EnergyConsumerResult result = new EnergyConsumerResult();
+ result.id = id;
+ result.energyUWs = (long) (energyUWs * (double) VOLTAGE_MV / 1000);
+ return new EnergyConsumerResult[]{result};
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
new file mode 100644
index 0000000..7bec13f6
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerAttribution;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.IntSupplier;
+
+public class CustomEnergyConsumerPowerStatsTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+ public static final int ENERGY_CONSUMER_ID1 = 77;
+ public static final int ENERGY_CONSUMER_ID2 = 88;
+ private static final int VOLTAGE_MV = 3500;
+ private static final double PRECISION = 0.00001;
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+ private static final EnergyConsumerPowerStatsLayout POWER_STATS_LAYOUT =
+ new EnergyConsumerPowerStatsLayout();
+
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+ private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
+
+ private EnergyConsumerPowerStatsCollector.Injector mInjector =
+ new EnergyConsumerPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> VOLTAGE_MV;
+ }
+ };
+
+
+ private CustomEnergyConsumerPowerStatsCollector mCollector;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mCollector = new CustomEnergyConsumerPowerStatsCollector(mInjector);
+ mCollector.setEnabled(true);
+ }
+
+ @Test
+ public void collectStats() throws Exception {
+ // Establish a baseline
+ collectPowerStats(0, 10_000, 30_000,
+ createAttribution(APP_UID1, 10_000),
+ createAttribution(APP_UID2, 15_000));
+
+ List<PowerStats> results = collectPowerStats(12345, 45_000, 100_000,
+ createAttribution(APP_UID1, 24_000),
+ createAttribution(APP_UID2, 36_000));
+
+ assertThat(results).hasSize(2);
+ PowerStats ps1 = results.stream()
+ .filter(ps -> ps.descriptor.name.equals("FOO")).findAny().orElseThrow();
+ assertThat(ps1.durationMs).isEqualTo(12345);
+
+ // Energy (uWs) / (voltage (mV) / 1000) -> charge (uC)
+ assertThat(POWER_STATS_LAYOUT.getConsumedEnergy(ps1.stats, 0))
+ .isEqualTo(10000);
+ assertThat(ps1.uidStats.size()).isEqualTo(0);
+
+ PowerStats ps2 = results.stream()
+ .filter(ps -> ps.descriptor.name.equals("BAR")).findAny().orElseThrow();
+ assertThat(POWER_STATS_LAYOUT.getConsumedEnergy(ps2.stats, 0))
+ .isEqualTo(20000);
+ assertThat(ps2.uidStats.size()).isEqualTo(2);
+ assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID1), 0))
+ .isEqualTo(14000);
+ assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID2), 0))
+ .isEqualTo(21000);
+ }
+
+ @Test
+ public void processStats() throws Exception {
+ AggregatedPowerStats aggregatedPowerStats = createAggregatedPowerStats();
+ aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ aggregatedPowerStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND,
+ 0);
+ aggregatedPowerStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ List<PowerStats> results = collectPowerStats(0, 10_000, 30_000,
+ createAttribution(APP_UID1, 10_000),
+ createAttribution(APP_UID2, 15_000));
+ for (PowerStats powerStats : results) {
+ aggregatedPowerStats.addPowerStats(powerStats, 0);
+ }
+
+ aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_BATTERY, 10_000);
+ aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 10_000);
+ aggregatedPowerStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
+ 10_000);
+ aggregatedPowerStats.setUidState(APP_UID2, STATE_PROCESS_STATE,
+ PROCESS_STATE_FOREGROUND_SERVICE, 10_000);
+
+ results = collectPowerStats(12345, 45_000, 100_000,
+ createAttribution(APP_UID1, 24_000),
+ createAttribution(APP_UID2, 36_000));
+ for (PowerStats powerStats : results) {
+ aggregatedPowerStats.addPowerStats(powerStats, 40_000);
+ }
+
+ aggregatedPowerStats.finish(40_0000);
+
+ List<PowerComponentAggregatedPowerStats> powerComponentStats =
+ aggregatedPowerStats.getPowerComponentStats();
+
+ PowerComponentAggregatedPowerStats ps1 = powerComponentStats.stream()
+ .filter(ps -> ps.getPowerStatsDescriptor().name.equals("FOO")).findAny()
+ .orElseThrow();
+ PowerComponentAggregatedPowerStats ps2 = powerComponentStats.stream()
+ .filter(ps -> ps.getPowerStatsDescriptor().name.equals("BAR")).findAny()
+ .orElseThrow();
+
+ long[] deviceStats = new long[ps1.getPowerStatsDescriptor().statsArrayLength];
+ long[] uidStats = new long[ps1.getPowerStatsDescriptor().uidStatsArrayLength];
+
+ // Total estimated power = 10,000 uC = 0.00278 mAh
+ double expectedPower = 0.00278;
+ ps1.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(expectedPower * 0.25);
+
+ ps1.getDeviceStats(deviceStats, states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER));
+ assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(expectedPower * 0.75);
+
+ // UID1: estimated power = 14,000 uC = 0.00388 mAh
+ expectedPower = 0.00388;
+ ps2.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower * 0.25);
+
+ ps2.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower * 0.75);
+
+ // UID2: estimated power = 21,000 uC = 0.00583 mAh
+ expectedPower = 0.00583;
+ ps2.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower * 0.25);
+
+ ps2.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower * 0.75);
+ }
+
+ private List<PowerStats> collectPowerStats(long timestamp, int chargeUc1, int chargeUc2,
+ EnergyConsumerAttribution... attributions2) throws Exception {
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.OTHER))
+ .thenReturn(new int[]{ENERGY_CONSUMER_ID1, ENERGY_CONSUMER_ID2});
+ when(mConsumedEnergyRetriever.getEnergyConsumerName(ENERGY_CONSUMER_ID1))
+ .thenReturn("FOO");
+ when(mConsumedEnergyRetriever.getEnergyConsumerName(ENERGY_CONSUMER_ID2))
+ .thenReturn("BAR");
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID1}))
+ .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID1, chargeUc1, null));
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID2}))
+ .thenReturn(
+ createEnergyConsumerResults(ENERGY_CONSUMER_ID2, chargeUc2, attributions2));
+
+ mStatsRule.setTime(timestamp, timestamp);
+ List<PowerStats> results = new ArrayList<>();
+ CountDownLatch latch = new CountDownLatch(2);
+ Consumer<PowerStats> consumer = powerStats -> {
+ results.add(powerStats);
+ latch.countDown();
+ };
+ mCollector.addConsumer(consumer);
+ mCollector.schedule();
+ assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
+ mCollector.removeConsumer(consumer);
+ return results;
+ }
+
+ private static AggregatedPowerStats createAggregatedPowerStats() {
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
+ .trackDeviceStates(
+ STATE_POWER,
+ STATE_SCREEN)
+ .trackUidStates(
+ STATE_POWER,
+ STATE_SCREEN,
+ STATE_PROCESS_STATE);
+
+ return new AggregatedPowerStats(config);
+ }
+
+ private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUws,
+ EnergyConsumerAttribution[] attributions) {
+ EnergyConsumerResult result = new EnergyConsumerResult();
+ result.id = id;
+ result.energyUWs = energyUws;
+ result.attribution = attributions;
+ return new EnergyConsumerResult[]{result};
+ }
+
+ private EnergyConsumerAttribution createAttribution(int uid, long energyUWs) {
+ EnergyConsumerAttribution attribution = new EnergyConsumerAttribution();
+ attribution.uid = uid;
+ attribution.energyUWs = energyUWs;
+ return attribution;
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
index 8a391c6..774be89 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
@@ -30,8 +30,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.location.GnssSignalQuality;
import android.os.BatteryConsumer;
@@ -119,8 +122,10 @@
@Test
public void powerProfileModel() {
// ODPM unsupported
- when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null))
+ when(mConsumedEnergyRetriever
+ .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
.thenReturn(new int[0]);
+
GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
mStatsRule.getPowerProfile(), mUidResolver);
@@ -209,7 +214,8 @@
@Test
public void energyConsumerModel() {
- when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null))
+ when(mConsumedEnergyRetriever
+ .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
.thenReturn(new int[]{ENERGY_CONSUMER_ID});
GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
mStatsRule.getPowerProfile(), mUidResolver);
@@ -224,8 +230,8 @@
collector.setEnabled(true);
// Establish a baseline
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{uCtoUj(10000)});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 10000));
collector.collectAndDeliverStats();
processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
@@ -237,8 +243,8 @@
processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{uCtoUj(2_170_000)});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 2_170_000));
collector.collectAndDeliverStats();
processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
@@ -247,8 +253,8 @@
processor.noteStateChange(stats, buildHistoryItem(8000,
GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
mStatsRule.setTime(11_000, 11_000);
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{uCtoUj(3_610_000)});
+ when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 3_610_000));
collector.collectAndDeliverStats();
processor.finish(stats, 11_000);
@@ -369,7 +375,10 @@
return powerComponentStats;
}
- private static long uCtoUj(long uc) {
- return (long) (uc * (double) VOLTAGE_MV / 1000);
+ private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUWs) {
+ EnergyConsumerResult result = new EnergyConsumerResult();
+ result.id = id;
+ result.energyUWs = (long) (energyUWs * (double) VOLTAGE_MV / 1000);
+ return new EnergyConsumerResult[]{result};
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
index 412fc88..32bfb2c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
@@ -79,6 +79,8 @@
private BatteryStatsHistory mHistory;
private CpuPowerStatsLayout mCpuStatsArrayLayout;
private PowerStats.Descriptor mPowerStatsDescriptor;
+ private final EnergyConsumerPowerStatsLayout mEnergyConsumerPowerStatsLayout =
+ new EnergyConsumerPowerStatsLayout();
@Before
public void setup() throws IOException {
@@ -87,14 +89,24 @@
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
- .trackDeviceStates(AggregatedPowerStatsConfig.STATE_POWER,
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
AggregatedPowerStatsConfig.STATE_SCREEN)
- .trackUidStates(AggregatedPowerStatsConfig.STATE_POWER,
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
new CpuPowerStatsProcessor(mStatsRule.getPowerProfile(),
mStatsRule.getCpuScalingPolicies()));
+ config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config);
mHistory = new BatteryStatsHistory(Parcel.obtain(), storeDirectory, 0, 10000,
@@ -120,15 +132,25 @@
@Test
public void breakdownByProcState_fullRange() throws Exception {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- new String[0], /* includePowerModels */ false,
+ new String[]{"cu570m"}, /* includePowerModels */ false,
/* includeProcessStateData */ true, /* powerThreshold */ 0);
exportAggregatedPowerStats(builder, 1000, 10000);
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 3.60);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 0.360);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
@@ -136,11 +158,17 @@
BatteryConsumer.PROCESS_STATE_FOREGROUND, 2.198082);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_BACKGROUND, 1.772916);
+ assertUidPowerEstimate(message, actual, APP_UID1,
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+ BatteryConsumer.PROCESS_STATE_ANY, 0.360);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.538999);
+ assertUidPowerEstimate(message, actual, APP_UID2,
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+ BatteryConsumer.PROCESS_STATE_ANY, 0);
actual.close();
}
@@ -148,15 +176,19 @@
@Test
public void breakdownByProcState_subRange() throws Exception {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- new String[0], /* includePowerModels */ false,
+ new String[]{"cu570m"}, /* includePowerModels */ false,
/* includeProcessStateData */ true, /* powerThreshold */ 0);
exportAggregatedPowerStats(builder, 3700, 6700);
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_ANY, 1.193332);
@@ -176,15 +208,19 @@
@Test
public void combinedProcessStates() throws Exception {
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- new String[0], /* includePowerModels */ false,
+ new String[]{"cu570m"}, /* includePowerModels */ false,
/* includeProcessStateData */ false, /* powerThreshold */ 0);
exportAggregatedPowerStats(builder, 1000, 10000);
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAggregatedPowerEstimate(message, actual,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
@@ -210,10 +246,19 @@
long[] uidStats2 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
powerStats.uidStats.put(APP_UID2, uidStats2);
+ PowerStats customPowerStats = new PowerStats(
+ new PowerStats.Descriptor(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+ "cu570m", mEnergyConsumerPowerStatsLayout.getDeviceStatsArrayLength(),
+ null, 0, mEnergyConsumerPowerStatsLayout.getUidStatsArrayLength(),
+ new PersistableBundle()));
+ long[] customUidStats = new long[mEnergyConsumerPowerStatsLayout.getUidStatsArrayLength()];
+ customPowerStats.uidStats.put(APP_UID1, customUidStats);
+
mHistory.forceRecordAllHistory();
mHistory.startRecordingHistory(1000, 1000, false);
mHistory.recordPowerStats(1000, 1000, powerStats);
+ mHistory.recordPowerStats(1000, 1000, customPowerStats);
mHistory.recordBatteryState(1000, 1000, 70, /* plugged */ false);
mHistory.recordStateStartEvent(1000, 1000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
mHistory.recordProcessStateChange(1000, 1000, APP_UID1,
@@ -245,6 +290,10 @@
mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 40000);
mHistory.recordPowerStats(6000, 6000, powerStats);
+ mEnergyConsumerPowerStatsLayout.setConsumedEnergy(customPowerStats.stats, 0, 3_600_000);
+ mEnergyConsumerPowerStatsLayout.setUidConsumedEnergy(customUidStats, 0, 360_000);
+ mHistory.recordPowerStats(6010, 6010, customPowerStats);
+
mPowerStatsAggregator.aggregatePowerStats(3500, 6500, stats -> {
mPowerStatsStore.storeAggregatedPowerStats(stats);
});
@@ -271,20 +320,13 @@
exporter.exportAggregatedPowerStats(builder, monotonicStartTime, monotonicEndTime);
}
- private void assertDevicePowerEstimate(String message, BatteryUsageStats bus, int componentId,
- double expected) {
- AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- assertWithMessage(message).that(consumer.getConsumedPower(componentId))
- .isWithin(TOLERANCE).of(expected);
- }
-
- private void assertAllAppsPowerEstimate(String message, BatteryUsageStats bus, int componentId,
- double expected) {
- AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
- assertWithMessage(message).that(consumer.getConsumedPower(componentId))
- .isWithin(TOLERANCE).of(expected);
+ private void assertAggregatedPowerEstimate(String message, BatteryUsageStats bus, int scope,
+ int componentId, double expected) {
+ AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(scope);
+ double actual = componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+ ? consumer.getConsumedPower(componentId)
+ : consumer.getConsumedPowerForCustomComponent(componentId);
+ assertWithMessage(message).that(actual).isWithin(TOLERANCE).of(expected);
}
private void assertUidPowerEstimate(String message, BatteryUsageStats bus, int uid,
@@ -293,9 +335,11 @@
final UidBatteryConsumer uidScope = uidScopes.stream()
.filter(us -> us.getUid() == uid).findFirst().orElse(null);
assertWithMessage(message).that(uidScope).isNotNull();
- assertWithMessage(message).that(uidScope.getConsumedPower(
- new BatteryConsumer.Dimensions(componentId, processState)))
- .isWithin(TOLERANCE).of(expected);
+ double actual = componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+ ? uidScope.getConsumedPower(
+ new BatteryConsumer.Dimensions(componentId, processState))
+ : uidScope.getConsumedPowerForCustomComponent(componentId);
+ assertWithMessage(message).that(actual).isWithin(TOLERANCE).of(expected);
}
private void clearDirectory(File dir) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 4e8c755..20b9592 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -25,6 +25,9 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG;
import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER;
@@ -597,7 +600,7 @@
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
- userState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME);
+ userState.updateShortcutTargetsLocked(Set.of(MAGNIFICATION_CONTROLLER_NAME), HARDWARE);
userState.setMagnificationCapabilitiesLocked(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -797,10 +800,11 @@
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
- userState.mAccessibilityShortcutKeyTargets.add(
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ userState.updateShortcutTargetsLocked(
+ Set.of(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()), HARDWARE);
mA11yms.performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE,
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mTestableLooper.processAllMessages();
@@ -815,10 +819,11 @@
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
- userState.mAccessibilityShortcutKeyTargets.add(
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ userState.updateShortcutTargetsLocked(
+ Set.of(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()), HARDWARE);
mA11yms.performAccessibilityShortcut(
+ Display.DEFAULT_DISPLAY, HARDWARE,
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
mTestableLooper.processAllMessages();
@@ -871,9 +876,10 @@
userState.mInstalledServices.clear();
userState.mInstalledServices.add(info_a);
userState.mInstalledServices.add(info_b);
- userState.mAccessibilityButtonTargets.clear();
- userState.mAccessibilityButtonTargets.add(info_a.getComponentName().flattenToString());
- userState.mAccessibilityButtonTargets.add(info_b.getComponentName().flattenToString());
+ userState.updateShortcutTargetsLocked(Set.of(
+ info_a.getComponentName().flattenToString(),
+ info_b.getComponentName().flattenToString()),
+ SOFTWARE);
// despite force stopping both packages, only the first service has the relevant flag,
// so only the first should be removed.
@@ -887,11 +893,11 @@
//Assert user state change
userState = mA11yms.getCurrentUserState();
- assertThat(userState.mAccessibilityButtonTargets).containsExactly(
+ assertThat(userState.getShortcutTargetsLocked(SOFTWARE)).containsExactly(
info_b.getComponentName().flattenToString());
//Assert setting change
final Set<String> targetsFromSetting = new ArraySet<>();
- mA11yms.readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ mA11yms.readColonDelimitedSettingToSet(ShortcutUtils.convertToKey(SOFTWARE),
userState.mUserId, str -> str, targetsFromSetting);
assertThat(targetsFromSetting).containsExactly(info_b.getComponentName().flattenToString());
}
@@ -992,10 +998,10 @@
final AccessibilityServiceInfo info_c = mockAccessibilityServiceInfo(
new ComponentName("package_c", "class_c"));
final AccessibilityUserState userState = mA11yms.getCurrentUserState();
- userState.mAccessibilityButtonTargets.clear();
- userState.mAccessibilityButtonTargets.add(info_b.getComponentName().flattenToString());
- userState.mAccessibilityShortcutKeyTargets.clear();
- userState.mAccessibilityShortcutKeyTargets.add(info_c.getComponentName().flattenToString());
+ userState.updateShortcutTargetsLocked(
+ Set.of(info_b.getComponentName().flattenToString()), SOFTWARE);
+ userState.updateShortcutTargetsLocked(
+ Set.of(info_c.getComponentName().flattenToString()), HARDWARE);
assertThat(mA11yms.isAccessibilityServiceWarningRequired(info_a)).isTrue();
assertThat(mA11yms.isAccessibilityServiceWarningRequired(info_b)).isFalse();
@@ -1082,7 +1088,7 @@
mA11yms.enableShortcutsForTargets(
/* enable= */ true,
- UserShortcutType.HARDWARE,
+ HARDWARE,
List.of(target),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
@@ -1346,14 +1352,14 @@
mA11yms.enableShortcutsForTargets(
/* enable= */ true,
- UserShortcutType.HARDWARE,
+ HARDWARE,
List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
- mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE,
+ mTestableContext, HARDWARE,
TARGET_STANDARD_A11Y_SERVICE.flattenToString())
).isTrue();
}
@@ -1367,7 +1373,7 @@
mA11yms.enableShortcutsForTargets(
/* enable= */ false,
- UserShortcutType.HARDWARE,
+ HARDWARE,
List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
@@ -1375,7 +1381,7 @@
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
mTestableContext,
- ShortcutConstants.UserShortcutType.HARDWARE,
+ HARDWARE,
TARGET_STANDARD_A11Y_SERVICE.flattenToString()))
.isFalse();
}
@@ -1390,14 +1396,14 @@
mA11yms.enableShortcutsForTargets(
/* enable= */ true,
- UserShortcutType.QUICK_SETTINGS,
+ QUICK_SETTINGS,
List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
- mTestableContext, UserShortcutType.QUICK_SETTINGS,
+ mTestableContext, QUICK_SETTINGS,
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
).isTrue();
verify(mStatusBarManagerInternal)
@@ -1417,14 +1423,14 @@
mA11yms.enableShortcutsForTargets(
/* enable= */ false,
- UserShortcutType.QUICK_SETTINGS,
+ QUICK_SETTINGS,
List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
mA11yms.getCurrentUserIdLocked());
mTestableLooper.processAllMessages();
assertThat(
ShortcutUtils.isComponentIdExistingInSettings(
- mTestableContext, UserShortcutType.QUICK_SETTINGS,
+ mTestableContext, QUICK_SETTINGS,
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
).isFalse();
verify(mStatusBarManagerInternal)
@@ -1556,8 +1562,8 @@
mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
setupShortcutTargetServices();
final AccessibilityUserState userState = mA11yms.getCurrentUserState();
- userState.mAccessibilityButtonTargets.clear();
- userState.mAccessibilityButtonTargets.add(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString());
+ userState.updateShortcutTargetsLocked(
+ Set.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), SOFTWARE);
ComponentName tile = new ComponentName(
TARGET_ALWAYS_ON_A11Y_SERVICE.getPackageName(),
TARGET_ALWAYS_ON_A11Y_SERVICE_TILE_CLASS);
@@ -1614,44 +1620,49 @@
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- public void restoreAccessibilityQsTargets_a11yQsTargetsRestored() {
+ public void restoreShortcutTargets_qs_a11yQsTargetsRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
String colorInversionTile =
AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
final AccessibilityUserState userState = new AccessibilityUserState(
UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
- userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
+ userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_QS_TARGETS,
- /*previousValue=*/null,
+ ShortcutUtils.convertToKey(QUICK_SETTINGS),
/*newValue=*/colorInversionTile);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM).getA11yQsTargets())
- .containsExactlyElementsIn(Set.of(daltonizerTile, colorInversionTile));
+ Set<String> expected = Set.of(daltonizerTile, colorInversionTile);
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
+ .containsExactlyElementsIn(expected);
+ assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS))
+ .containsExactlyElementsIn(expected);
}
@Test
@DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
- public void restoreAccessibilityQsTargets_a11yQsTargetsNotRestored() {
+ public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() {
String daltonizerTile =
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
String colorInversionTile =
AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
final AccessibilityUserState userState = new AccessibilityUserState(
UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
- userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
+ userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
+ putShortcutSettingForUser(QUICK_SETTINGS, daltonizerTile, userState.mUserId);
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_QS_TARGETS,
- /*previousValue=*/null,
+ ShortcutUtils.convertToKey(QUICK_SETTINGS),
/*newValue=*/colorInversionTile);
- assertThat(userState.getA11yQsTargets())
- .containsExactlyElementsIn(Set.of(daltonizerTile));
+ Set<String> expected = Set.of(daltonizerTile);
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
+ .containsExactlyElementsIn(expected);
+ assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS))
+ .containsExactlyElementsIn(expected);
}
@Test
@@ -1717,27 +1728,26 @@
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE)
- public void restoreA11yShortcutTargetService_targetsMerged() {
+ public void restoreShortcutTargets_hardware_targetsMerged() {
+ mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
final String otherPrevious = TARGET_MAGNIFICATION;
- final String combinedPrevious = String.join(":", servicePrevious, otherPrevious);
final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
final AccessibilityUserState userState = new AccessibilityUserState(
UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
setupShortcutTargetServices(userState);
+ mA11yms.enableShortcutsForTargets(
+ true, HARDWARE, List.of(servicePrevious, otherPrevious), userState.mUserId);
broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- /*previousValue=*/combinedPrevious,
+ ShortcutUtils.convertToKey(HARDWARE),
/*newValue=*/serviceRestored);
final Set<String> expected = Set.of(servicePrevious, otherPrevious, serviceRestored);
- assertThat(readStringsFromSetting(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
.containsExactlyElementsIn(expected);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
- .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
.containsExactlyElementsIn(expected);
}
@@ -1745,7 +1755,7 @@
@EnableFlags({
android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
- public void restoreA11yShortcutTargetService_alreadyHadDefaultService_doesNotClear() {
+ public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
mTestableContext.getOrCreateTestableResources().addOverride(
R.string.config_defaultAccessibilityService, serviceDefault);
@@ -1754,17 +1764,18 @@
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
setupShortcutTargetServices(userState);
+ // default is present in userState & setting, so it's not cleared
+ putShortcutSettingForUser(HARDWARE, serviceDefault, UserHandle.USER_SYSTEM);
+ userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE);
+
broadcastSettingRestored(
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- /*previousValue=*/serviceDefault,
/*newValue=*/serviceDefault);
final Set<String> expected = Set.of(serviceDefault);
- assertThat(readStringsFromSetting(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
.containsExactlyElementsIn(expected);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
- .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
.containsExactlyElementsIn(expected);
}
@@ -1772,7 +1783,7 @@
@EnableFlags({
android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
- public void restoreA11yShortcutTargetService_didNotHaveDefaultService_clearsDefaultService() {
+ public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
// Restored value from the broadcast contains both default and non-default service.
@@ -1784,18 +1795,45 @@
mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
setupShortcutTargetServices(userState);
- broadcastSettingRestored(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- /*previousValue=*/null,
+ broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
/*newValue=*/combinedRestored);
// The default service is cleared from the final restored value.
final Set<String> expected = Set.of(serviceRestored);
- assertThat(readStringsFromSetting(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
.containsExactlyElementsIn(expected);
- assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
- .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
+ .containsExactlyElementsIn(expected);
+ }
+
+ @Test
+ @EnableFlags({
+ android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
+ Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+ public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
+ final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+ final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
+ // Restored value from the broadcast contains both default and non-default service.
+ final String combinedRestored = String.join(":", serviceDefault, serviceRestored);
+ mTestableContext.getOrCreateTestableResources().addOverride(
+ R.string.config_defaultAccessibilityService, serviceDefault);
+ final AccessibilityUserState userState = new AccessibilityUserState(
+ UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+ mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+ setupShortcutTargetServices(userState);
+
+ // UserState has default, but setting is null (this emulates a typical scenario in SUW).
+ userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE);
+ putShortcutSettingForUser(HARDWARE, null, UserHandle.USER_SYSTEM);
+
+ broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
+ /*newValue=*/combinedRestored);
+
+ // The default service is cleared from the final restored value.
+ final Set<String> expected = Set.of(serviceRestored);
+ assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
+ .containsExactlyElementsIn(expected);
+ assertThat(userState.getShortcutTargetsLocked(HARDWARE))
.containsExactlyElementsIn(expected);
}
@@ -1806,11 +1844,10 @@
return result;
}
- private void broadcastSettingRestored(String setting, String previousValue, String newValue) {
+ private void broadcastSettingRestored(String setting, String newValue) {
Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.putExtra(Intent.EXTRA_SETTING_NAME, setting)
- .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue)
.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, newValue);
sendBroadcastToAccessibilityManagerService(intent);
mTestableLooper.processAllMessages();
@@ -1952,4 +1989,13 @@
private static boolean isSameCurrentUser(AccessibilityManagerService service, Context context) {
return service.getCurrentUserIdLocked() == context.getUserId();
}
+
+ private void putShortcutSettingForUser(@UserShortcutType int shortcutType,
+ String shortcutValue, int userId) {
+ Settings.Secure.putStringForUser(
+ mTestableContext.getContentResolver(),
+ ShortcutUtils.convertToKey(shortcutType),
+ shortcutValue,
+ userId);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b269beb9..123f0ed 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -28,6 +28,9 @@
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
import static com.google.common.truth.Truth.assertThat;
@@ -158,8 +161,8 @@
mUserState.setInteractiveUiTimeoutLocked(30);
mUserState.mEnabledServices.add(COMPONENT_NAME);
mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
- mUserState.mAccessibilityShortcutKeyTargets.add(COMPONENT_NAME.flattenToString());
- mUserState.mAccessibilityButtonTargets.add(COMPONENT_NAME.flattenToString());
+ mUserState.updateShortcutTargetsLocked(Set.of(COMPONENT_NAME.flattenToString()), HARDWARE);
+ mUserState.updateShortcutTargetsLocked(Set.of(COMPONENT_NAME.flattenToString()), SOFTWARE);
mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString());
mUserState.setTouchExplorationEnabledLocked(true);
mUserState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
@@ -181,8 +184,8 @@
assertEquals(0, mUserState.getInteractiveUiTimeoutLocked());
assertTrue(mUserState.mEnabledServices.isEmpty());
assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
- assertTrue(mUserState.mAccessibilityShortcutKeyTargets.isEmpty());
- assertTrue(mUserState.mAccessibilityButtonTargets.isEmpty());
+ assertTrue(mUserState.getShortcutTargetsLocked(HARDWARE).isEmpty());
+ assertTrue(mUserState.getShortcutTargetsLocked(SOFTWARE).isEmpty());
assertNull(mUserState.getTargetAssignedToAccessibilityButton());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
assertFalse(mUserState.isMagnificationSingleFingerTripleTapEnabledLocked());
@@ -429,20 +432,20 @@
}
@Test
- public void updateA11yQsTargetLocked_valueUpdated() {
+ public void updateShortcutTargetsLocked_quickSettings_valueUpdated() {
Set<String> newTargets = Set.of(
AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(),
AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString()
);
- mUserState.updateA11yQsTargetLocked(newTargets);
+ mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS);
assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets);
}
@Test
public void getA11yQsTargets_returnsCopiedData() {
- updateA11yQsTargetLocked_valueUpdated();
+ updateShortcutTargetsLocked_quickSettings_valueUpdated();
Set<String> targets = mUserState.getA11yQsTargets();
targets.clear();
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 30e3b18..dbab54b 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -672,6 +672,61 @@
new HashSet<>(mUserController.getRunningUsersLU()));
}
+ /** Test scheduling stopping of background users - reschedule if current user is a guest. */
+ @Test
+ public void testScheduleStopOfBackgroundUser_rescheduleWhenGuest() throws Exception {
+ mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
+ /* backgroundUserScheduledStopTimeSecs= */ 2);
+
+ final int TEST_USER_GUEST = 902;
+ setUpUser(TEST_USER_GUEST, UserInfo.FLAG_GUEST);
+
+ setUpUser(TEST_USER_ID2, NO_USERINFO_FLAGS);
+
+ // Switch to TEST_USER_ID from user 0
+ int numberOfUserSwitches = 0;
+ addForegroundUserAndContinueUserSwitch(TEST_USER_ID, UserHandle.USER_SYSTEM,
+ ++numberOfUserSwitches, false,
+ /* expectScheduleBackgroundUserStopping= */ false);
+ assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID),
+ mUserController.getRunningUsersLU());
+
+ // Switch to TEST_USER_GUEST from TEST_USER_ID
+ addForegroundUserAndContinueUserSwitch(TEST_USER_GUEST, TEST_USER_ID,
+ ++numberOfUserSwitches, false,
+ /* expectScheduleBackgroundUserStopping= */ true);
+ assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_GUEST),
+ mUserController.getRunningUsersLU());
+
+ // Allow the post-switch processing to complete.
+ // TEST_USER_ID may be scheduled for stopping, but it shouldn't actually stop since the
+ // current user is a Guest.
+ assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
+ assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_GUEST);
+ assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_GUEST),
+ mUserController.getRunningUsersLU());
+
+ // Switch to TEST_USER_ID2 from TEST_USER_GUEST
+ // Guests are automatically stopped in the background, so it won't be scheduled.
+ addForegroundUserAndContinueUserSwitch(TEST_USER_ID2, TEST_USER_GUEST,
+ ++numberOfUserSwitches, true,
+ /* expectScheduleBackgroundUserStopping= */ false);
+ assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID, TEST_USER_ID2),
+ mUserController.getRunningUsersLU());
+
+ // Allow the post-switch processing to complete.
+ // TEST_USER_ID should *still* be scheduled for stopping, since we skipped stopping it
+ // earlier.
+ assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
+ assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_GUEST);
+ assertAndProcessScheduledStopBackgroundUser(false, TEST_USER_ID2);
+ assertEquals(Arrays.asList(SYSTEM_USER_ID, TEST_USER_ID2),
+ mUserController.getRunningUsersLU());
+ }
+
/**
* Process queued SCHEDULED_STOP_BACKGROUND_USER_MSG message, if expected.
* @param userId the user we are checking to see whether it is scheduled.
@@ -682,11 +737,11 @@
boolean expectScheduled, @Nullable Integer userId) {
TestHandler handler = mInjector.mHandler;
if (expectScheduled) {
- assertTrue(handler.hasMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
+ assertTrue(handler.hasEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
handler.removeMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId);
mUserController.processScheduledStopOfBackgroundUser(userId);
} else {
- assertFalse(handler.hasMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
+ assertFalse(handler.hasEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, userId));
}
}
@@ -1534,9 +1589,9 @@
mInjector.mHandler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
- assertEquals(mInjector.mHandler
- .hasMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, expectedOldUserId),
- expectScheduleBackgroundUserStopping);
+ assertEquals(expectScheduleBackgroundUserStopping,
+ mInjector.mHandler
+ .hasEqualMessages(SCHEDULED_STOP_BACKGROUND_USER_MSG, expectedOldUserId));
verify(mInjector, times(expectedNumberOfCalls)).dismissUserSwitchingDialog(any());
continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping,
expectScheduleBackgroundUserStopping);
@@ -1810,6 +1865,13 @@
}
private static class TestHandler extends Handler {
+ /**
+ * Keeps an accessible copy of messages that were queued for us to query.
+ *
+ * WARNING: queued messages get added to this, but processed/removed messages to NOT
+ * automatically get removed. This can lead to confusing bugs. Maybe one day someone will
+ * fix this, but in the meantime, this is your warning.
+ */
private final List<Message> mMessages = new ArrayList<>();
TestHandler(Looper looper) {
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 8a7815e..c34e28f 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -47,7 +47,9 @@
import android.os.Handler;
import android.os.UserHandle;
import android.test.InstrumentationTestCase;
+import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.SparseArray;
import android.util.Xml;
import android.widget.RemoteViews;
@@ -67,10 +69,12 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Random;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
@@ -388,6 +392,93 @@
assertThat(target.previewLayout).isEqualTo(original.previewLayout);
}
+ public void testBackupRestoreControllerStatePersistence() throws IOException {
+ // Setup mock data
+ final Set<String> mockPrunedApps = getMockPrunedApps();
+ final SparseArray<
+ List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>
+ > mockUpdatesByProvider = getMockUpdates();
+ final SparseArray<
+ List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>
+ > mockUpdatesByHost = getMockUpdates();
+ final AppWidgetServiceImpl.BackupRestoreController.State state =
+ new AppWidgetServiceImpl.BackupRestoreController.State(
+ mockPrunedApps, mockUpdatesByProvider, mockUpdatesByHost);
+
+ final File file = new File(mTestContext.getDataDir(), "state.xml");
+ saveBackupRestoreControllerState(file, state);
+ final AppWidgetServiceImpl.BackupRestoreController.State target =
+ loadStateLocked(file);
+ assertNotNull(target);
+ final SparseArray<List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>>
+ actualUpdatesByProvider = target.getUpdatesByProvider();
+ assertNotNull(actualUpdatesByProvider);
+ final SparseArray<List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>>
+ actualUpdatesByHost = target.getUpdatesByHost();
+ assertNotNull(actualUpdatesByHost);
+
+ assertEquals(mockPrunedApps, target.getPrunedApps());
+ for (int i = 0; i < mockUpdatesByProvider.size(); i++) {
+ final int key = mockUpdatesByProvider.keyAt(i);
+ verifyRestoreUpdateRecord(
+ actualUpdatesByProvider.get(key), mockUpdatesByProvider.get(key));
+ }
+ for (int i = 0; i < mockUpdatesByHost.size(); i++) {
+ final int key = mockUpdatesByHost.keyAt(i);
+ verifyRestoreUpdateRecord(
+ actualUpdatesByHost.get(key), mockUpdatesByHost.get(key));
+ }
+ }
+
+ private void verifyRestoreUpdateRecord(
+ @NonNull final List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>
+ actualUpdates,
+ @NonNull final List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>
+ expectedUpdates) {
+ assertEquals(expectedUpdates.size(), actualUpdates.size());
+ for (int i = 0; i < expectedUpdates.size(); i++) {
+ final AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord expected =
+ expectedUpdates.get(i);
+ final AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord actual =
+ actualUpdates.get(i);
+ assertEquals(expected.oldId, actual.oldId);
+ assertEquals(expected.newId, actual.newId);
+ assertEquals(expected.notified, actual.notified);
+ }
+ }
+
+ @NonNull
+ private static Set<String> getMockPrunedApps() {
+ final Set<String> mockPrunedApps = new ArraySet<>(10);
+ for (int i = 0; i < 10; i++) {
+ mockPrunedApps.add("com.example.app" + i);
+ }
+ return mockPrunedApps;
+ }
+
+ @NonNull
+ private static SparseArray<
+ List<AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>
+ > getMockUpdates() {
+ final SparseArray<List<
+ AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord>> ret =
+ new SparseArray<>(4);
+ ret.put(0, new ArrayList<>());
+ for (int i = 0; i < 5; i++) {
+ final AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord record =
+ new AppWidgetServiceImpl.BackupRestoreController.RestoreUpdateRecord(
+ 5 - i, i);
+ record.notified = (i % 2 == 1);
+ final int key = (i < 3) ? 1 : 2;
+ if (!ret.contains(key)) {
+ ret.put(key, new ArrayList<>());
+ }
+ ret.get(key).add(record);
+ }
+ ret.put(3, new ArrayList<>());
+ return ret;
+ }
+
private int setupHostAndWidget() {
List<PendingHostUpdate> updates = mService.startListening(
mMockHost, mPkgName, HOST_ID, new int[0]).getList();
@@ -418,6 +509,40 @@
return mTestContext.getResources().getInteger(resId);
}
+ private static void saveBackupRestoreControllerState(
+ @NonNull final File dst,
+ @Nullable final AppWidgetServiceImpl.BackupRestoreController.State state)
+ throws IOException {
+ Objects.requireNonNull(dst);
+ if (state == null) {
+ return;
+ }
+ final AtomicFile file = new AtomicFile(dst);
+ final FileOutputStream stream = file.startWrite();
+ final TypedXmlSerializer out = Xml.resolveSerializer(stream);
+ out.startDocument(null, true);
+ AppWidgetXmlUtil.writeBackupRestoreControllerState(out, state);
+ out.endDocument();
+ file.finishWrite(stream);
+ }
+
+ private static AppWidgetServiceImpl.BackupRestoreController.State loadStateLocked(
+ @NonNull final File dst) {
+ Objects.requireNonNull(dst);
+ final AtomicFile file = new AtomicFile(dst);
+ try (FileInputStream stream = file.openRead()) {
+ final TypedXmlPullParser parser = Xml.resolvePullParser(stream);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // drain whitespace, comments, etc.
+ }
+ return AppWidgetXmlUtil.readBackupRestoreControllerState(parser);
+ } catch (IOException | XmlPullParserException e) {
+ return null;
+ }
+ }
+
private static void saveWidgetProviderInfoLocked(@NonNull final File dst,
@Nullable final AppWidgetProviderInfo info)
throws IOException {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 503ab8e..0f38532 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -79,6 +79,9 @@
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.security.GateKeeper;
import android.security.KeyStoreAuthorization;
@@ -118,6 +121,8 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final String TEST_PACKAGE_NAME = "test_package";
private static final long TEST_REQUEST_ID = 44;
@@ -1506,6 +1511,75 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCanAuthenticate_whenMandatoryBiometricsRequested()
+ throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
+ mBiometricService.onStart();
+
+ when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+ when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+ .thenReturn(true);
+
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
+
+ when(mTrustManager.isInSignificantPlace()).thenReturn(true);
+
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCanAuthenticate_whenMandatoryBiometricsAndStrongAuthenticatorsRequested()
+ throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
+ mBiometricService.onStart();
+
+ when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+ when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+ .thenReturn(true);
+
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS
+ | Authenticators.BIOMETRIC_STRONG));
+
+ when(mTrustManager.isInSignificantPlace()).thenReturn(true);
+
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS
+ | Authenticators.BIOMETRIC_STRONG));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testCanAuthenticate_whenMandatoryBiometricsRequestedAndDeviceCredentialAvailable()
+ throws Exception {
+ mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
+ mBiometricService.onStart();
+
+ when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+ when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+ .thenReturn(true);
+
+ setupAuthForOnly(TYPE_CREDENTIAL, Authenticators.DEVICE_CREDENTIAL);
+
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
+
+ when(mTrustManager.isInSignificantPlace()).thenReturn(true);
+
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS
+ | Authenticators.DEVICE_CREDENTIAL));
+ }
+
+ @Test
public void testAuthenticatorActualStrength() {
// Tuple of OEM config, updatedStrength, and expectedStrength
final int[][] testCases = {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index b40d7ee..b831ef5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -32,11 +32,16 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.content.Context;
+import android.content.res.Resources;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.PromptInfo;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.filters.SmallTest;
@@ -54,6 +59,9 @@
public class PreAuthInfoTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
private static final int SENSOR_ID_FINGERPRINT = 0;
private static final int SENSOR_ID_FACE = 1;
@@ -66,6 +74,8 @@
@Mock
Context mContext;
@Mock
+ Resources mResources;
+ @Mock
ITrustManager mTrustManager;
@Mock
DevicePolicyManager mDevicePolicyManager;
@@ -80,6 +90,7 @@
when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
+ when(mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt())).thenReturn(true);
when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
@@ -91,6 +102,8 @@
.thenReturn(LOCKOUT_NONE);
when(mBiometricCameraManager.isCameraPrivacyEnabled()).thenReturn(false);
when(mBiometricCameraManager.isAnyCameraUnavailable()).thenReturn(false);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getString(anyInt())).thenReturn(TEST_PACKAGE_NAME);
}
@Test
@@ -184,6 +197,54 @@
assertThat(preAuthInfo.eligibleSensors.get(0).modality).isEqualTo(TYPE_FINGERPRINT);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testMandatoryBiometricsStatus_whenAllRequirementsSatisfiedAndSensorAvailable()
+ throws Exception {
+ when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+
+ final BiometricSensor sensor = getFaceSensor();
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.eligibleSensors).hasSize(1);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testMandatoryBiometricsStatus_whenAllRequirementsSatisfiedAndSensorUnavailable()
+ throws Exception {
+ when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+ final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.eligibleSensors).hasSize(0);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+ public void testMandatoryBiometricsStatus_whenRequirementsNotSatisfiedAndSensorAvailable()
+ throws Exception {
+ when(mTrustManager.isInSignificantPlace()).thenReturn(true);
+
+ final BiometricSensor sensor = getFaceSensor();
+ final PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS
+ | BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.eligibleSensors).hasSize(1);
+ }
+
private BiometricSensor getFingerprintSensor() {
BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index b05a819..cb75e1a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -179,7 +179,8 @@
// The rest of the bits are not allowed to integrate with the public APIs
for (int i = 8; i < 32; i++) {
final int authenticator = 1 << i;
- if (authenticator == Authenticators.DEVICE_CREDENTIAL) {
+ if (authenticator == Authenticators.DEVICE_CREDENTIAL
+ || authenticator == Authenticators.MANDATORY_BIOMETRICS) {
continue;
}
assertFalse(Utils.isValidAuthenticatorConfig(1 << i));
diff --git a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
index c165c66..d8fd266a 100644
--- a/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/ApplicationInfoBuilder.java
@@ -21,8 +21,10 @@
class ApplicationInfoBuilder {
private boolean mIsDebuggable;
private int mTargetSdk;
+ private int mUid;
private String mPackageName;
private long mVersionCode;
+ private boolean mIsSystemApp;
private ApplicationInfoBuilder() {
mTargetSdk = -1;
@@ -42,6 +44,16 @@
return this;
}
+ ApplicationInfoBuilder systemApp() {
+ mIsSystemApp = true;
+ return this;
+ }
+
+ ApplicationInfoBuilder withUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+
ApplicationInfoBuilder withPackageName(String packageName) {
mPackageName = packageName;
return this;
@@ -60,6 +72,10 @@
applicationInfo.packageName = mPackageName;
applicationInfo.targetSdkVersion = mTargetSdk;
applicationInfo.longVersionCode = mVersionCode;
+ applicationInfo.uid = mUid;
+ if (mIsSystemApp) {
+ applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ }
return applicationInfo;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 9accd49..9df7a36 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -29,6 +29,7 @@
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Build;
@@ -36,6 +37,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.LocalServices;
@@ -65,6 +67,8 @@
CompatConfig mCompatConfig;
@Mock
private AndroidBuildClassifier mBuildClassifier;
+ @Mock
+ private ChangeReporter mChangeReporter;
@Before
public void setUp() throws Exception {
@@ -78,7 +82,8 @@
when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
+ mPlatformCompat =
+ new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
// Assume userdebug/eng non-final build
mCompatConfig.forceNonDebuggableFinalForTest(false);
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
@@ -100,7 +105,8 @@
.addLoggingOnlyChangeWithId(7L)
.addDisabledOverridableChangeWithId(8L)
.build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
+ mPlatformCompat =
+ new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -128,7 +134,8 @@
.addLoggingOnlyChangeWithId(7L)
.addEnableSinceSdkChangeWithId(31, 8L)
.build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
+ mPlatformCompat =
+ new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -146,7 +153,8 @@
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
.build();
mCompatConfig.forceNonDebuggableFinalForTest(true);
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
+ mPlatformCompat =
+ new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
// Before adding overrides.
assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();
@@ -369,4 +377,68 @@
// Listener not called when a non existing override is removed.
verify(mListener1, never()).onCompatChange(PACKAGE_NAME);
}
+
+ @Test
+ public void testReportChange() throws Exception {
+ ApplicationInfo appInfo = ApplicationInfoBuilder.create().withUid(123).build();
+ mPlatformCompat.reportChange(1L, appInfo);
+ verify(mChangeReporter).reportChange(123, 1L, ChangeReporter.STATE_LOGGED, false, true);
+
+ ApplicationInfo systemAppInfo =
+ ApplicationInfoBuilder.create().withUid(123).systemApp().build();
+ mPlatformCompat.reportChange(1L, systemAppInfo);
+ verify(mChangeReporter).reportChange(123, 1L, ChangeReporter.STATE_LOGGED, true, true);
+ }
+
+ @Test
+ public void testReportChangeByPackageName() throws Exception {
+ when(mPackageManagerInternal.getApplicationInfo(
+ eq(PACKAGE_NAME), eq(0L), anyInt(), anyInt()))
+ .thenReturn(
+ ApplicationInfoBuilder.create()
+ .withPackageName(PACKAGE_NAME)
+ .withUid(123)
+ .build());
+
+ mPlatformCompat.reportChangeByPackageName(1L, PACKAGE_NAME, 123);
+ verify(mChangeReporter).reportChange(123, 1L, ChangeReporter.STATE_LOGGED, false, true);
+
+ String SYSTEM_PACKAGE_NAME = "my.system.package";
+
+ when(mPackageManagerInternal.getApplicationInfo(
+ eq(SYSTEM_PACKAGE_NAME), eq(0L), anyInt(), anyInt()))
+ .thenReturn(
+ ApplicationInfoBuilder.create()
+ .withPackageName(SYSTEM_PACKAGE_NAME)
+ .withUid(123)
+ .systemApp()
+ .build());
+
+ mPlatformCompat.reportChangeByPackageName(1L, SYSTEM_PACKAGE_NAME, 123);
+ verify(mChangeReporter).reportChange(123, 1L, ChangeReporter.STATE_LOGGED, true, true);
+ }
+
+ @Test
+ public void testIsChangeEnabled() throws Exception {
+ mCompatConfig =
+ CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(1L)
+ .addDisabledChangeWithId(2L)
+ .addEnabledChangeWithId(3L)
+ .build();
+ mCompatConfig.forceNonDebuggableFinalForTest(true);
+ mPlatformCompat =
+ new PlatformCompat(mContext, mCompatConfig, mBuildClassifier, mChangeReporter);
+
+ ApplicationInfo appInfo = ApplicationInfoBuilder.create().withUid(123).build();
+ assertThat(mPlatformCompat.isChangeEnabled(1L, appInfo)).isTrue();
+ verify(mChangeReporter).reportChange(123, 1L, ChangeReporter.STATE_ENABLED, false, false);
+ assertThat(mPlatformCompat.isChangeEnabled(2L, appInfo)).isFalse();
+ verify(mChangeReporter).reportChange(123, 2L, ChangeReporter.STATE_DISABLED, false, false);
+
+ ApplicationInfo systemAppInfo =
+ ApplicationInfoBuilder.create().withUid(123).systemApp().build();
+ assertThat(mPlatformCompat.isChangeEnabled(3L, systemAppInfo)).isTrue();
+ verify(mChangeReporter).reportChange(123, 3L, ChangeReporter.STATE_ENABLED, true, false);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index f9077c4..93fc071a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -196,11 +196,6 @@
}
@Override
- void setKeystorePassword(byte[] password, int userHandle) {
-
- }
-
- @Override
void initKeystoreSuperKeys(int userId, SyntheticPassword sp, boolean allowExisting) {
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java
index ddb9b43..58a1d9c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java
@@ -16,12 +16,15 @@
package com.android.server.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import android.app.Application;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.testing.AndroidTestingRunner;
@@ -66,6 +69,12 @@
}
@Test
+ public void getComponent_returnsComponent() {
+ assertThat(mService.getComponent()).isEqualTo(new ComponentName("android",
+ "com.android.server.notification.CountdownConditionProvider"));
+ }
+
+ @Test
public void testGetPendingIntent() {
PendingIntent pi = mService.getPendingIntent(Uri.EMPTY);
assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
index 4c440ca..4af96ef 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
@@ -16,14 +16,16 @@
package com.android.server.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import android.app.Application;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Intent;
-import android.net.Uri;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -63,7 +65,13 @@
service.onCreate();
service.onBind(startIntent);
mService = spy(service);
- }
+ }
+
+ @Test
+ public void getComponent_returnsComponent() {
+ assertThat(mService.getComponent()).isEqualTo(new ComponentName("android",
+ "com.android.server.notification.EventConditionProvider"));
+ }
@Test
public void testGetPendingIntent() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 398dc281..c48d745 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -40,6 +40,7 @@
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
import static android.app.Notification.VISIBILITY_PRIVATE;
+import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -91,7 +92,9 @@
import static android.service.notification.Adjustment.KEY_CONTEXTUAL_ACTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
+import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
+import static android.service.notification.Adjustment.TYPE_NEWS;
import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_TRUE;
@@ -14332,6 +14335,29 @@
@Test
@EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+ public void enqueueNotification_directlyThroughRunnable_populatesAllowlistToken() {
+ Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentIntent(createPendingIntent("content"))
+ .build();
+ NotificationRecord record = new NotificationRecord(
+ mContext,
+ new StatusBarNotification(mPkg, mPkg, 1, "tag", mUid, 44, receivedWithoutParceling,
+ mUser, "groupKey", 0),
+ mTestNotificationChannel);
+ assertThat(record.getNotification().getAllowlistToken()).isNull();
+
+ mWorkerHandler.post(
+ mService.new EnqueueNotificationRunnable(mUserId, record, false, false,
+ mPostNotificationTrackerFactory.newTracker(null)));
+ waitForIdle();
+
+ assertThat(mService.mNotificationList).hasSize(1);
+ assertThat(mService.mNotificationList.get(0).getNotification().getAllowlistToken())
+ .isEqualTo(NotificationManagerService.ALLOWLIST_TOKEN);
+ }
+
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
public void enqueueNotification_rejectsOtherToken() throws RemoteException {
Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentIntent(createPendingIntent("content"))
@@ -15778,4 +15804,27 @@
when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
assertThat(mBinderService.getEffectsSuppressor()).isEqualTo(mListener.component);
}
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testApplyAdjustment_keyType_validType() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+
+ Bundle signals = new Bundle();
+ signals.putInt(KEY_TYPE, TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+
+ waitForIdle();
+
+ r.applyAdjustments();
+
+ assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 594d6f2..6a99731 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -37,6 +37,7 @@
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RemoteViews;
@@ -105,7 +106,7 @@
private static final ImmutableSet<Class<?>> UNUSABLE_TYPES =
ImmutableSet.of(Consumer.class, IBinder.class, MediaSession.Token.class, Parcel.class,
PrintWriter.class, Resources.Theme.class, View.class,
- LayoutInflater.Factory2.class);
+ LayoutInflater.Factory2.class, ProtoOutputStream.class);
// Maximum number of times we allow generating the same class recursively.
// E.g. new RemoteViews.addView(new RemoteViews()) but stop there.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index d1880d2..ff4bc21 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -23,6 +23,10 @@
import static android.app.NotificationChannel.ALLOW_BUBBLE_ON;
import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationChannel.DEFAULT_ALLOW_BUBBLE;
+import static android.app.NotificationChannel.NEWS_ID;
+import static android.app.NotificationChannel.PROMOTIONS_ID;
+import static android.app.NotificationChannel.RECS_ID;
+import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationChannel.USER_LOCKED_LIGHTS;
@@ -47,6 +51,9 @@
import static android.os.UserHandle.USER_SYSTEM;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
+import static android.service.notification.Flags.notificationClassification;
+
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
@@ -184,6 +191,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
+@EnableFlags(FLAG_PERSIST_INCOMPLETE_RESTORE_DATA)
public class PreferencesHelperTest extends UiServiceTestCase {
private static final int UID_HEADLESS = 1000000;
private static final UserHandle USER = UserHandle.of(0);
@@ -233,7 +241,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(
- FLAG_PERSIST_INCOMPLETE_RESTORE_DATA);
+ FLAG_NOTIFICATION_CLASSIFICATION);
}
public PreferencesHelperTest(FlagsParameterization flags) {
@@ -582,6 +590,16 @@
assertTrue(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false,
UID_N_MR1, false));
+ NotificationChannel updateNews = null;
+ if (notificationClassification()) {
+ // change one of the reserved bundle channels to ensure changes are persisted across
+ // boot
+ updateNews = mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, NEWS_ID, false).copy();
+ updateNews.setImportance(IMPORTANCE_NONE);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, updateNews, true, 1000, true);
+ }
+
mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
@@ -597,6 +615,12 @@
mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+ if (notificationClassification()) {
+ assertThat(mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, updateNews.getId(),
+ false)).isNotNull();
+ assertThat(mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, updateNews.getId(),
+ false)).isEqualTo(updateNews);
+ }
List<NotificationChannelGroup> actualGroups = mXmlHelper.getNotificationChannelGroups(
PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
@@ -1130,9 +1154,22 @@
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\" uid=\"10002\">\n"
+ + (notificationClassification() ? "<channel id=\"android.app.social\" "
+ + "name=\"Social\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
+ + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
+ "<package name=\"com.example.n_mr1\" show_badge=\"true\" "
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
@@ -1140,6 +1177,10 @@
+ "=\"false\" uid=\"10001\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.social\" "
+ + "name=\"Social\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1149,14 +1190,22 @@
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
+ + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ "</package>\n"
+ "<package name=\"com.example.p\" show_badge=\"true\" "
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"true\" sent_valid_msg=\"true\""
- + " user_demote_msg_app=\"true\" sent_valid_bubble=\"false\" uid=\"10003\" />\n"
- + "</ranking>";
+ + " user_demote_msg_app=\"true\" sent_valid_bubble=\"false\" uid=\"10003\"";
assertThat(baos.toString()).contains(expected);
}
@@ -1216,9 +1265,22 @@
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\">\n"
+ + (notificationClassification() ? "<channel id=\"android.app.social\" "
+ + "name=\"Social\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
+ + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
// Importance default because on in permission helper
+ "<package name=\"com.example.n_mr1\" importance=\"3\" show_badge=\"true\" "
@@ -1227,6 +1289,10 @@
+ "=\"false\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.social\" "
+ + "name=\"Social\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1236,6 +1302,15 @@
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
+ + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1243,12 +1318,11 @@
// Importance default because on in permission helper
+ "<package name=\"com.example.p\" importance=\"3\" show_badge=\"true\" "
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"true\" sent_valid_msg=\"true\""
- + " user_demote_msg_app=\"true\" sent_valid_bubble=\"false\" />\n"
- // Packages that exist solely in permissionhelper
- + "<package name=\"first\" importance=\"3\" />\n"
- + "<package name=\"third\" importance=\"0\" />\n"
- + "</ranking>";
+ + " user_demote_msg_app=\"true\" sent_valid_bubble=\"false\"";
assertThat(baos.toString()).contains(expected);
+ // Packages that exist solely in permissionhelper
+ assertThat(baos.toString()).contains("<package name=\"first\" importance=\"3\"");
+ assertThat(baos.toString()).contains("<package name=\"third\" importance=\"0\"");
}
@Test
@@ -1303,9 +1377,22 @@
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\">\n"
+ + (notificationClassification() ? "<channel id=\"android.app.social\" "
+ + "name=\"Social\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
+ + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
// Importance 0 because missing from permission helper
+ "<package name=\"com.example.n_mr1\" importance=\"0\" show_badge=\"true\" "
@@ -1314,6 +1401,10 @@
+ "=\"false\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.social\" "
+ + "name=\"Social\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1323,6 +1414,15 @@
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
+ + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
+ + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
+ + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1330,8 +1430,7 @@
// Importance default because on in permission helper
+ "<package name=\"com.example.p\" importance=\"3\" show_badge=\"true\" "
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"true\" sent_valid_msg=\"true\""
- + " user_demote_msg_app=\"true\" sent_valid_bubble=\"false\" />\n"
- + "</ranking>";
+ + " user_demote_msg_app=\"true\" sent_valid_bubble=\"false\"";
assertThat(baos.toString()).contains(expected);
}
@@ -1851,8 +1950,8 @@
parser.nextTag();
mHelper.readXml(parser, false, UserHandle.USER_ALL);
- final NotificationChannel updated1 =
- mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ final NotificationChannel updated1 = mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
assertTrue(updated1.canBypassDnd());
assertEquals(VISIBILITY_SECRET, updated1.getLockscreenVisibility());
@@ -2392,18 +2491,20 @@
// Returns only non-deleted channels
List<NotificationChannel> channels =
mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList();
- assertEquals(2, channels.size()); // Default channel + non-deleted channel
+ // Default channel + non-deleted channel + system defaults
+ assertEquals(notificationClassification() ? 6 : 2, channels.size());
for (NotificationChannel nc : channels) {
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ if (channel2.getId().equals(nc.getId())) {
compareChannels(channel2, nc);
}
}
// Returns deleted channels too
channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList();
- assertEquals(3, channels.size()); // Includes default channel
+ // Includes system channel(s)
+ assertEquals(notificationClassification() ? 7 : 3, channels.size());
for (NotificationChannel nc : channels) {
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ if (channel2.getId().equals(nc.getId())) {
compareChannels(channelMap.get(nc.getId()), nc);
}
}
@@ -3013,7 +3114,8 @@
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt()))
+ .thenReturn(legacy);
// create records with the default channel for all user 0 and user 1 uids
mHelper.canShowBadge(PKG_N_MR1, user0Uids[i]);
@@ -3024,13 +3126,14 @@
// user 0 records remain
for (int i = 0; i < user0Uids.length; i++) {
- assertEquals(1,
- mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false).getList().size());
+ assertEquals(notificationClassification() ? 5 : 1,
+ mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false)
+ .getList().size());
}
// user 1 records are gone
for (int i = 0; i < user1Uids.length; i++) {
- assertEquals(0,
- mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false).getList().size());
+ assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false)
+ .getList().size());
}
}
@@ -3054,7 +3157,8 @@
assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM,
new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}));
- assertEquals(2, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+ assertEquals(notificationClassification() ? 6 : 2,
+ mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
}
@Test
@@ -3126,7 +3230,8 @@
@Test
public void testRecordDefaults() throws Exception {
assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
- assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+ assertEquals(notificationClassification() ? 5 : 1,
+ mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
}
@Test
@@ -3212,7 +3317,9 @@
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
- assertEquals(2, group.getChannels().size()); // misc channel too
+ assertEquals(
+ notificationClassification() ? 6 : 2,
+ group.getChannels().size()); // misc channel too
assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
|| channel3.getId().equals(group.getChannels().get(1).getId()));
} else if (group.getId().equals(ncg.getId())) {
@@ -3363,6 +3470,9 @@
new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false,
UID_N_MR1, false);
}
+ if (notificationClassification()) {
+ numChannels += 4;
+ }
expectedChannels.put(pkgName, numChannels);
}
@@ -4583,7 +4693,12 @@
@Test
public void testTooManyChannels() {
- for (int i = 0; i < NOTIFICATION_CHANNEL_COUNT_LIMIT; i++) {
+ int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
+ if (notificationClassification()) {
+ // reserved channels lower limit
+ numToCreate -= 4;
+ }
+ for (int i = 0; i < numToCreate; i++) {
NotificationChannel channel = new NotificationChannel(String.valueOf(i),
String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true, UID_O, false);
@@ -4602,11 +4717,16 @@
@Test
public void testTooManyChannels_xml() throws Exception {
+ int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
+ if (notificationClassification()) {
+ // reserved channels lower limit
+ numToCreate -= 4;
+ }
String extraChannel = "EXTRA";
String extraChannel1 = "EXTRA1";
// create first... many... directly so we don't need a big xml blob in this test
- for (int i = 0; i < NOTIFICATION_CHANNEL_COUNT_LIMIT; i++) {
+ for (int i = 0; i < numToCreate; i++) {
NotificationChannel channel = new NotificationChannel(String.valueOf(i),
String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true, UID_O, false);
@@ -5619,8 +5739,9 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- // number of channels with preferences should be 3 total
- assertEquals("expected number of events", 3, events.size());
+ assertEquals("expected number of events",
+ notificationClassification() ? 7 : 3,
+ events.size());
for (StatsEvent ev : events) {
// all of these events should be of PackageNotificationChannelPreferences type,
// and therefore we expect the atom to have this field.
@@ -5643,7 +5764,7 @@
} else if (eventChannelId.equals("a")) {
assertEquals("channel name", "a", p.getChannelName());
assertEquals("importance", IMPORTANCE_LOW, p.getImportance());
- } else { // b
+ } else if (eventChannelId.equals("b")){ // b
assertEquals("channel name", "b", p.getChannelName());
assertEquals("importance", IMPORTANCE_HIGH, p.getImportance());
}
@@ -5661,11 +5782,17 @@
mHelper.createNotificationChannel(PKG_O, UID_O, channelC, true, false, UID_O, false);
List<String> channels = new LinkedList<>(Arrays.asList("a", "b", "c"));
+ if (notificationClassification()) {
+ channels.add(NEWS_ID);
+ channels.add(PROMOTIONS_ID);
+ channels.add(SOCIAL_MEDIA_ID);
+ channels.add(RECS_ID);
+ }
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events", 3, events.size());
+ assertEquals("total events", notificationClassification() ? 7 : 3, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -5699,7 +5826,7 @@
mHelper.pullPackageChannelPreferencesStats(events);
// In this case, we want to check the properties of the conversation channel (not parent)
- assertEquals("total events", 2, events.size());
+ assertEquals("total events", notificationClassification() ? 6 : 2, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -5731,7 +5858,9 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events", 2, events.size());
+ assertEquals("total events",
+ notificationClassification() ? 6 : 2,
+ events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -5762,7 +5891,9 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events", 2, events.size());
+ assertEquals("total events",
+ notificationClassification() ? 6 : 2,
+ events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6044,6 +6175,23 @@
assertEquals(0, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
}
+ @Test
+ @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testNotificationBundles() {
+ // do something that triggers settings creation for an app
+ mHelper.setShowBadge(PKG_O, UID_O, true);
+
+ // verify 4 reserved channels are created
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, NEWS_ID, false).getImportance())
+ .isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false)
+ .getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false)
+ .getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, RECS_ID, false).getImportance())
+ .isEqualTo(IMPORTANCE_LOW);
+ }
+
private static NotificationChannel cloneChannel(NotificationChannel original) {
Parcel parcel = Parcel.obtain();
try {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
index 7d89d87..fe4ce465e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -1,5 +1,7 @@
package com.android.server.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -8,6 +10,7 @@
import android.app.Application;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.service.notification.Condition;
@@ -58,6 +61,12 @@
}
@Test
+ public void getComponent_returnsComponent() {
+ assertThat(mService.getComponent()).isEqualTo(new ComponentName("android",
+ "com.android.server.notification.ScheduleConditionProvider"));
+ }
+
+ @Test
public void testIsValidConditionId_incomplete() throws Exception {
Uri badConditionId = Uri.EMPTY;
assertFalse(mService.isValidConditionId(badConditionId));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 9352c12..f5ab95c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -511,6 +511,9 @@
rule.iconResName = ICON_RES_NAME;
rule.triggerDescription = TRIGGER_DESC;
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
+ if (Flags.modesUi()) {
+ rule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_USER;
+ }
Parcel parcel = Parcel.obtain();
rule.writeToParcel(parcel, 0);
@@ -540,6 +543,9 @@
assertEquals(rule.triggerDescription, parceled.triggerDescription);
assertEquals(rule.zenPolicy, parceled.zenPolicy);
assertEquals(rule.deletionInstant, parceled.deletionInstant);
+ if (Flags.modesUi()) {
+ assertEquals(rule.disabledOrigin, parceled.disabledOrigin);
+ }
assertEquals(rule, parceled);
assertEquals(rule.hashCode(), parceled.hashCode());
@@ -620,6 +626,9 @@
rule.iconResName = ICON_RES_NAME;
rule.triggerDescription = TRIGGER_DESC;
rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
+ if (Flags.modesUi()) {
+ rule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_APP;
+ }
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeRuleXml(rule, baos);
@@ -653,6 +662,9 @@
assertEquals(rule.triggerDescription, fromXml.triggerDescription);
assertEquals(rule.iconResName, fromXml.iconResName);
assertEquals(rule.deletionInstant, fromXml.deletionInstant);
+ if (Flags.modesUi()) {
+ assertEquals(rule.disabledOrigin, fromXml.disabledOrigin);
+ }
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 26a13cb..57587f7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.Flags.FLAG_MODES_API;
import static android.app.Flags.FLAG_MODES_UI;
import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +33,7 @@
import android.app.NotificationManager;
import android.content.ComponentName;
import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -79,16 +81,17 @@
: Set.of("version", "manualRule", "automaticRules");
// Differences for flagged fields are only generated if the flag is enabled.
- // "Metadata" fields (userModifiedFields & co, deletionInstant) are not compared.
+ // "Metadata" fields (userModifiedFields, deletionInstant, disabledOrigin) are not compared.
private static final Set<String> ZEN_RULE_EXEMPT_FIELDS =
android.app.Flags.modesApi()
? Set.of("userModifiedFields", "zenPolicyUserModifiedFields",
- "zenDeviceEffectsUserModifiedFields", "deletionInstant")
+ "zenDeviceEffectsUserModifiedFields", "deletionInstant",
+ "disabledOrigin")
: Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, "userModifiedFields",
"zenPolicyUserModifiedFields", "zenDeviceEffectsUserModifiedFields",
- "deletionInstant");
+ "deletionInstant", "disabledOrigin");
// allowPriorityChannels is flagged by android.app.modes_api
public static final Set<String> ZEN_MODE_CONFIG_FLAGGED_FIELDS =
@@ -201,8 +204,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testConfigDiff_fieldDiffs_flagOn() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
// these two start the same
ZenModeConfig c1 = new ZenModeConfig();
ZenModeConfig c2 = new ZenModeConfig();
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 201b286..7bb633e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -43,7 +43,6 @@
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
-import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND;
import static android.app.NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -5013,6 +5012,48 @@
}
@Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void updateAutomaticZenRule_changeOwnerForSystemRule_allowed() {
+ when(mContext.checkCallingPermission(anyString()))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ AutomaticZenRule original = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName("android", "some.old.cps"))
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule("android", original,
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reason", Process.SYSTEM_UID);
+
+ AutomaticZenRule update = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName("android", "brand.new.cps"))
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId, update, UPDATE_ORIGIN_USER, "reason",
+ Process.SYSTEM_UID);
+
+ AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(result).isNotNull();
+ assertThat(result.getOwner().getClassName()).isEqualTo("brand.new.cps");
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void updateAutomaticZenRule_changeOwnerForAppOwnedRule_ignored() {
+ AutomaticZenRule original = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName(mContext.getPackageName(), "old.third.party.cps"))
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), original,
+ UPDATE_ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+
+ AutomaticZenRule update = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName(mContext.getPackageName(), "new.third.party.cps"))
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId, update, UPDATE_ORIGIN_USER, "reason",
+ Process.SYSTEM_UID);
+
+ AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(ruleId);
+ assertThat(result).isNotNull();
+ assertThat(result.getOwner().getClassName()).isEqualTo("old.third.party.cps");
+ }
+
+ @Test
@EnableFlags(FLAG_MODES_API)
public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
@@ -6185,6 +6226,101 @@
assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects);
}
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void addAutomaticZenRule_startsDisabled_recordsDisabledOrigin() {
+ AutomaticZenRule startsDisabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName(mPkg, "SomeProvider"))
+ .setEnabled(false)
+ .build();
+
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsDisabled, UPDATE_ORIGIN_APP,
+ "new", CUSTOM_PKG_UID);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
+ UPDATE_ORIGIN_APP);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void updateAutomaticZenRule_disabling_recordsDisabledOrigin() {
+ AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName(mPkg, "SomeProvider"))
+ .setEnabled(true)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled, UPDATE_ORIGIN_APP,
+ "new", CUSTOM_PKG_UID);
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
+ UPDATE_ORIGIN_UNKNOWN);
+
+ AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled)
+ .setEnabled(false)
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, UPDATE_ORIGIN_USER, "off",
+ Process.SYSTEM_UID);
+
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
+ UPDATE_ORIGIN_USER);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void updateAutomaticZenRule_keepingDisabled_preservesPreviousDisabledOrigin() {
+ AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName(mPkg, "SomeProvider"))
+ .setEnabled(true)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled, UPDATE_ORIGIN_APP,
+ "new", CUSTOM_PKG_UID);
+ AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled)
+ .setEnabled(false)
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, UPDATE_ORIGIN_USER, "off",
+ Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
+ UPDATE_ORIGIN_USER);
+
+ // Now update it again, for an unrelated reason with a different origin.
+ AutomaticZenRule nowRenamed = new AutomaticZenRule.Builder(nowDisabled)
+ .setName("Fancy pants rule")
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId, nowRenamed, UPDATE_ORIGIN_APP, "update",
+ CUSTOM_PKG_UID);
+
+ // Identity of the disabler is preserved.
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
+ UPDATE_ORIGIN_USER);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void updateAutomaticZenRule_enabling_clearsDisabledOrigin() {
+ AutomaticZenRule startsEnabled = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
+ .setOwner(new ComponentName(mPkg, "SomeProvider"))
+ .setEnabled(true)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, startsEnabled, UPDATE_ORIGIN_APP,
+ "new", CUSTOM_PKG_UID);
+ AutomaticZenRule nowDisabled = new AutomaticZenRule.Builder(startsEnabled)
+ .setEnabled(false)
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId, nowDisabled, UPDATE_ORIGIN_USER, "off",
+ Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
+ UPDATE_ORIGIN_USER);
+
+ // Now enable it again
+ AutomaticZenRule nowEnabled = new AutomaticZenRule.Builder(nowDisabled)
+ .setEnabled(true)
+ .build();
+ mZenModeHelper.updateAutomaticZenRule(ruleId, nowEnabled, UPDATE_ORIGIN_APP, "on",
+ CUSTOM_PKG_UID);
+
+ // Identity of the disabler was cleared.
+ assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).disabledOrigin).isEqualTo(
+ UPDATE_ORIGIN_UNKNOWN);
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index d6c0fef..1875284 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -781,6 +781,34 @@
}
@Test
+ @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS)
+ public void vibrate_thenDeniedAppOps_getsCancelled() throws Throwable {
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+
+ var vib = vibrate(service,
+ VibrationEffect.createWaveform(new long[]{100, 100, 100, 100}, 0), RINGTONE_ATTRS);
+
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ when(mAppOpsManagerMock.checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_NOTIFICATION_RINGTONE), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+
+ service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null);
+
+ assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+
+ var statsInfoCaptor = ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
+ verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
+ .writeVibrationReportedAsync(statsInfoCaptor.capture());
+
+ VibrationStats.StatsInfo touchMetrics = statsInfoCaptor.getAllValues().get(0);
+ assertEquals(Vibration.Status.CANCELLED_BY_APP_OPS.getProtoEnumValue(),
+ touchMetrics.status);
+ }
+
+ @Test
public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
VibratorManagerService service = createSystemReadyService();
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 2e571bb..aa28147 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -132,8 +132,9 @@
KeyboardLogEvent.VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN, 0},
{"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
KeyboardLogEvent.VOLUME_MUTE, KeyEvent.KEYCODE_VOLUME_MUTE, 0},
- {"ALL_APPS key -> Open App Drawer", new int[]{KeyEvent.KEYCODE_ALL_APPS},
- KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_ALL_APPS, 0},
+ {"ALL_APPS key -> Open App Drawer in Accessibility mode",
+ new int[]{KeyEvent.KEYCODE_ALL_APPS},
+ KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, KeyEvent.KEYCODE_ALL_APPS, 0},
{"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
KeyboardLogEvent.LAUNCH_SEARCH, KeyEvent.KEYCODE_SEARCH, 0},
{"LANGUAGE_SWITCH key -> Switch Keyboard Language",
@@ -259,14 +260,15 @@
{"Long press META + H -> Launch assistant",
new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H, META_ON},
- {"Long press HOME key -> Open App Drawer",
+ {"Long press HOME key -> Open App Drawer in Accessibility mode",
new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
- KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_HOME, 0},
- {"Long press META + ENTER -> Open App Drawer",
+ KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, KeyEvent.KEYCODE_HOME, 0},
+ {"Long press META + ENTER -> Open App Drawer in Accessibility mode",
new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
- KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_ENTER, META_ON},
- {"Long press META + H -> Open App Drawer", new int[]{META_KEY, KeyEvent.KEYCODE_H},
- LONG_PRESS_HOME_ALL_APPS, KeyboardLogEvent.ALL_APPS,
+ KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, KeyEvent.KEYCODE_ENTER, META_ON},
+ {"Long press META + H -> Open App Drawer in Accessibility mode",
+ new int[]{META_KEY, KeyEvent.KEYCODE_H},
+ LONG_PRESS_HOME_ALL_APPS, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS,
KeyEvent.KEYCODE_H, META_ON}};
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index e91fd37..ef3df6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -263,16 +263,10 @@
doAnswer((InvocationOnMock invocationOnMock) -> {
final ClientTransaction transaction = invocationOnMock.getArgument(0);
final List<ClientTransactionItem> items = transaction.getTransactionItems();
- if (items != null) {
- for (ClientTransactionItem item : items) {
- if (item instanceof PauseActivityItem) {
- pauseFound.value = true;
- break;
- }
- }
- } else {
- if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) {
+ for (ClientTransactionItem item : items) {
+ if (item instanceof PauseActivityItem) {
pauseFound.value = true;
+ break;
}
}
return null;
@@ -1639,6 +1633,15 @@
clearInvocations(mDefaultDisplay);
}
+ @Test
+ public void testCompleteResume_updateCompatDisplayInsets() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ doReturn(true).when(activity).shouldCreateCompatDisplayInsets();
+ activity.setState(RESUMED, "test");
+ activity.completeResumeLocked();
+ assertNotNull(activity.getCompatDisplayInsets());
+ }
+
/**
* Verify destroy activity request completes successfully.
*/
@@ -3089,6 +3092,30 @@
}
@Test
+ public void testOnStartingWindowDrawn() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ // The task-has-been-visible should not affect the decision of making transition ready.
+ activity.getTask().setHasBeenVisible(true);
+ activity.detachFromProcess();
+ activity.mStartingData = mock(StartingData.class);
+ registerTestTransitionPlayer();
+ final Transition transition = activity.mTransitionController.requestTransitionIfNeeded(
+ WindowManager.TRANSIT_OPEN, 0 /* flags */, null /* trigger */, mDisplayContent);
+ activity.onStartingWindowDrawn();
+ assertTrue(activity.mStartingData.mIsDisplayed);
+ // The transition can be ready by the starting window of a visible-requested activity
+ // without a running process.
+ assertTrue(transition.allReady());
+
+ // If other event makes the transition unready, the reentrant of onStartingWindowDrawn
+ // should not replace the readiness again.
+ transition.setReady(mDisplayContent, false);
+ activity.onStartingWindowDrawn();
+ assertFalse(transition.allReady());
+ }
+
+
+ @Test
public void testCloseToSquareFixedOrientation() {
if (Flags.insetsDecoupledConfiguration()) {
// No test needed as decor insets no longer affects orientation.
@@ -3324,14 +3351,8 @@
// to client if the app didn't request IME visible.
assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
- if (Flags.bundleClientTransactionFlag()) {
- verify(app2.getProcess(), atLeastOnce()).scheduleClientTransactionItem(
- isA(WindowStateResizeItem.class));
- } else {
- verify(app2.mClient, atLeastOnce()).resized(any(), anyBoolean(), any(),
- insetsStateCaptor.capture(), anyBoolean(), anyBoolean(), anyInt(), anyInt(),
- anyBoolean(), any());
- }
+ verify(app2.getProcess(), atLeastOnce()).scheduleClientTransactionItem(
+ isA(WindowStateResizeItem.class));
assertFalse(app2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
new file mode 100644
index 0000000..5f2853a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.wm.utils.TestComponentStack;
+
+import org.junit.Assert;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Robot implementation for {@link ActivityRecord}.
+ */
+class AppCompatActivityRobot {
+
+ private static final int DEFAULT_DISPLAY_WIDTH = 1000;
+ private static final int DEFAULT_DISPLAY_HEIGHT = 2000;
+
+ private static final float DELTA_ASPECT_RATIO_TOLERANCE = 0.0001f;
+ private static final float COMPAT_SCALE_TOLERANCE = 0.0001f;
+
+ private static final String TEST_COMPONENT_NAME = AppCompatActivityRobot.class.getName();
+
+ @NonNull
+ private final ActivityTaskManagerService mAtm;
+ @NonNull
+ private final ActivityTaskSupervisor mSupervisor;
+ @NonNull
+ private final TestComponentStack<ActivityRecord> mActivityStack;
+ @NonNull
+ private final TestComponentStack<Task> mTaskStack;
+
+ private final int mDisplayWidth;
+ private final int mDisplayHeight;
+
+ AppCompatActivityRobot(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor,
+ int displayWidth, int displayHeight) {
+ mAtm = atm;
+ mSupervisor = supervisor;
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ mActivityStack = new TestComponentStack<>();
+ mTaskStack = new TestComponentStack<>();
+ }
+
+ AppCompatActivityRobot(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor) {
+ this(wm, atm, supervisor, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ }
+
+ void createActivityWithComponent() {
+ createActivityWithComponentInNewTask(/* inNewTask */ mTaskStack.isEmpty());
+ }
+
+ void createActivityWithComponentInNewTask() {
+ createActivityWithComponentInNewTask(/* inNewTask */ true);
+ }
+
+ void configureTopActivity(float minAspect, float maxAspect, int screenOrientation,
+ boolean isUnresizable) {
+ prepareLimitedBounds(mActivityStack.top(), minAspect, maxAspect, screenOrientation,
+ isUnresizable);
+ }
+
+ void configureTopActivityIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+ mActivityStack.top().mDisplayContent
+ .setIgnoreOrientationRequest(ignoreOrientationRequest);
+ }
+
+ void configureUnresizableTopActivity(@ActivityInfo.ScreenOrientation int screenOrientation) {
+ configureTopActivity(/* minAspect */ -1, /* maxAspect */ -1, screenOrientation,
+ /* isUnresizable */ true);
+ }
+
+ @NonNull
+ ActivityRecord top() {
+ return mActivityStack.top();
+ }
+
+ @NonNull
+ ActivityRecord getFromTop(int fromTop) {
+ return mActivityStack.getFromTop(fromTop);
+ }
+
+ void setTaskWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
+ mTaskStack.top().setWindowingMode(windowingMode);
+ }
+
+ void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
+ doReturn(enabled).when(mActivityStack.top())
+ .isLetterboxedForFixedOrientationAndAspectRatio();
+ }
+
+ void enableTreatmentForTopActivity(boolean enabled) {
+ doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
+ .isTreatmentEnabledForActivity(eq(mActivityStack.top()));
+ }
+
+ void setTopActivityCameraActive(boolean enabled) {
+ doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
+ .isCameraActive(eq(mActivityStack.top()), /* mustBeFullscreen= */ eq(true));
+ }
+
+ void setTopActivityEligibleForOrientationOverride(boolean enabled) {
+ doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
+ .isActivityEligibleForOrientationOverride(eq(mActivityStack.top()));
+ }
+
+ void setShouldApplyUserMinAspectRatioOverride(boolean enabled) {
+ doReturn(enabled).when(mActivityStack.top()
+ .mLetterboxUiController).shouldApplyUserMinAspectRatioOverride();
+ }
+
+ void setShouldApplyUserFullscreenOverride(boolean enabled) {
+ doReturn(enabled).when(mActivityStack.top()
+ .mLetterboxUiController).shouldApplyUserFullscreenOverride();
+ }
+
+ void setGetUserMinAspectRatioOverrideCode(@PackageManager.UserMinAspectRatio int orientation) {
+ doReturn(orientation).when(mActivityStack.top()
+ .mLetterboxUiController).getUserMinAspectRatioOverrideCode();
+ }
+
+ void setIgnoreOrientationRequest(boolean enabled) {
+ mActivityStack.top().mDisplayContent.setIgnoreOrientationRequest(enabled);
+ }
+
+ void setTopActivityAsEmbedded(boolean embedded) {
+ doReturn(embedded).when(mActivityStack.top()).isEmbedded();
+ }
+
+ void destroyTopActivity() {
+ mActivityStack.top().removeImmediately();
+ }
+
+ void destroyActivity(int fromTop) {
+ mActivityStack.applyTo(/* fromTop */ fromTop, ActivityRecord::removeImmediately);
+ }
+
+ void createNewTask() {
+ final DisplayContent displayContent = new TestDisplayContent
+ .Builder(mAtm, mDisplayWidth, mDisplayHeight).build();
+ final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
+ .setDisplay(displayContent).build();
+ mTaskStack.push(newTask);
+ }
+
+ void createNewTaskWithBaseActivity() {
+ final DisplayContent displayContent = new TestDisplayContent
+ .Builder(mAtm, mDisplayWidth, mDisplayHeight).build();
+ final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
+ .setCreateActivity(true)
+ .setDisplay(displayContent).build();
+ mTaskStack.push(newTask);
+ pushActivity(newTask.getTopNonFinishingActivity());
+ }
+
+ void attachTopActivityToTask() {
+ mTaskStack.top().addChild(mActivityStack.top());
+ }
+
+ void applyToTopActivity(Consumer<ActivityRecord> consumer) {
+ consumer.accept(mActivityStack.top());
+ }
+
+ void applyToActivity(int fromTop, @NonNull Consumer<ActivityRecord> consumer) {
+ mActivityStack.applyTo(fromTop, consumer);
+ }
+
+ void applyToAllActivities(@NonNull Consumer<ActivityRecord> consumer) {
+ mActivityStack.applyToAll(consumer);
+ }
+
+ void rotateDisplayForTopActivity(@Surface.Rotation int rotation) {
+ rotateDisplay(mActivityStack.top().mDisplayContent, rotation);
+ }
+
+ void configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
+ mActivityStack.applyToTop((topActivity) -> {
+ final DisplayRotation r = topActivity.mDisplayContent.getDisplayRotation();
+ doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
+ doReturn(false).when(r)
+ .isDeviceInPosture(any(DeviceStateController.DeviceState.class),
+ anyBoolean());
+ if (isHalfFolded) {
+ doReturn(true).when(r)
+ .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED,
+ isTabletop);
+ }
+ topActivity.recomputeConfiguration();
+ });
+ }
+
+ private static void rotateDisplay(@Surface.Rotation DisplayContent display, int rotation) {
+ final Configuration c = new Configuration();
+ display.getDisplayRotation().setRotation(rotation);
+ display.computeScreenConfiguration(c);
+ display.onRequestedOverrideConfigurationChanged(c);
+ }
+
+ void assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
+ mActivityStack.applyTo(fromTop, (activity) -> {
+ Assert.assertTrue(predicate.test(activity));
+ });
+ }
+
+ void assertFalseOnTopActivity(Predicate<ActivityRecord> predicate) {
+ Assert.assertFalse(predicate.test(mActivityStack.top()));
+ }
+
+ void assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
+ mActivityStack.applyTo(fromTop, (activity) -> {
+ Assert.assertFalse(predicate.test(activity));
+ });
+ }
+
+ void assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter) {
+ Assert.assertNotNull(getter.apply(mActivityStack.top()));
+ }
+
+ void assertNullOnTopActivity(Function<ActivityRecord, Object> getter) {
+ Assert.assertNull(getter.apply(mActivityStack.top()));
+ }
+
+ void checkTopActivityRecomputedConfiguration() {
+ verify(mActivityStack.top()).recomputeConfiguration();
+ }
+
+ void checkTopActivityConfigOrientation(@Configuration.Orientation int orientation) {
+ Assert.assertEquals(orientation, mActivityStack.top()
+ .getRequestedConfigurationOrientation());
+ }
+
+ void checkTopActivityAspectRatios(float minAspectRatio, float maxAspectRatio) {
+ final ActivityRecord topActivity = mActivityStack.top();
+ Assert.assertEquals(minAspectRatio, topActivity.getMinAspectRatio(),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ Assert.assertEquals(maxAspectRatio, topActivity.getMaxAspectRatio(),
+ DELTA_ASPECT_RATIO_TOLERANCE);
+ }
+
+ void checkTopActivityInSizeCompatMode(boolean inScm) {
+ final ActivityRecord topActivity = mActivityStack.top();
+ Assert.assertEquals(inScm, topActivity.inSizeCompatMode());
+ Assert.assertNotEquals(1f, topActivity.getCompatScale(), COMPAT_SCALE_TOLERANCE);
+ }
+
+ void launchActivity(float minAspectRatio, float maxAspectRatio,
+ @ActivityInfo.ScreenOrientation int orientation, boolean transparent,
+ boolean withComponent, boolean addToTask) {
+ final WindowTestsBase.ActivityBuilder
+ activityBuilder = new WindowTestsBase.ActivityBuilder(mAtm)
+ .setScreenOrientation(orientation)
+ .setLaunchedFromUid(0);
+ if (transparent) {
+ activityBuilder.setActivityTheme(android.R.style.Theme_Translucent);
+ }
+ if (withComponent) {
+ // Set the component to be that of the test class in order
+ // to enable compat changes
+ activityBuilder.setComponent(ComponentName.createRelative(mAtm.mContext,
+ TEST_COMPONENT_NAME));
+ }
+ if (minAspectRatio >= 0) {
+ activityBuilder.setMinAspectRatio(minAspectRatio);
+ }
+ if (maxAspectRatio >= 0) {
+ activityBuilder.setMaxAspectRatio(maxAspectRatio);
+ }
+ final ActivityRecord newActivity = activityBuilder.build();
+ if (addToTask) {
+ if (mTaskStack.isEmpty()) {
+ createNewTask();
+ }
+ mTaskStack.top().addChild(newActivity);
+ }
+ pushActivity(newActivity);
+ }
+
+ private void createActivityWithComponentInNewTask(boolean inNewTask) {
+ if (inNewTask) {
+ createNewTask();
+ }
+ final ActivityRecord activity = new WindowTestsBase.ActivityBuilder(mAtm)
+ .setOnTop(true)
+ .setTask(mTaskStack.top())
+ // Set the component to be that of the test class in order
+ // to enable compat changes
+ .setComponent(ComponentName.createRelative(mAtm.mContext, TEST_COMPONENT_NAME))
+ .build();
+ pushActivity(activity);
+ }
+
+ /**
+ * Setups activity with restriction on its bounds, such as maxAspect, minAspect,
+ * fixed orientation, and/or whether it is resizable.
+ */
+ private void prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect,
+ @ActivityInfo.ScreenOrientation int screenOrientation, boolean isUnresizable) {
+ activity.info.resizeMode = isUnresizable
+ ? RESIZE_MODE_UNRESIZEABLE
+ : RESIZE_MODE_RESIZEABLE;
+ final Task task = activity.getTask();
+ if (task != null) {
+ // Update the Task resize value as activity will follow the task.
+ task.mResizeMode = activity.info.resizeMode;
+ task.getRootActivity().info.resizeMode = activity.info.resizeMode;
+ }
+ activity.setVisibleRequested(true);
+ if (maxAspect >= 0) {
+ activity.info.setMaxAspectRatio(maxAspect);
+ }
+ if (minAspect >= 0) {
+ activity.info.setMinAspectRatio(minAspect);
+ }
+ if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+ activity.info.screenOrientation = screenOrientation;
+ activity.setRequestedOrientation(screenOrientation);
+ }
+ // Make sure to use the provided configuration to construct the size compat fields.
+ activity.clearSizeCompatMode();
+ activity.ensureActivityConfiguration();
+ // Make sure the display configuration reflects the change of activity.
+ if (activity.mDisplayContent.updateOrientation()) {
+ activity.mDisplayContent.sendNewConfiguration();
+ }
+ }
+
+ private DisplayRotationCompatPolicy getTopDisplayRotationCompatPolicy() {
+ return mActivityStack.top().mDisplayContent.mDisplayRotationCompatPolicy;
+ }
+
+ // We add the activity to the stack and spyOn() on its properties.
+ private void pushActivity(@NonNull ActivityRecord activity) {
+ mActivityStack.push(activity);
+ spyOn(activity);
+ spyOn(activity.mAppCompatController.getTransparentPolicy());
+ if (activity.mDisplayContent != null
+ && activity.mDisplayContent.mDisplayRotationCompatPolicy != null) {
+ spyOn(activity.mDisplayContent.mDisplayRotationCompatPolicy);
+ }
+ spyOn(activity.mLetterboxUiController);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
new file mode 100644
index 0000000..d568eec
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.content.pm.PackageManager;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Robot class for optIn/optOut properties.
+ */
+class AppCompatComponentPropRobot {
+ @NonNull
+ private final WindowManagerService mWm;
+
+ AppCompatComponentPropRobot(@NonNull WindowManagerService wm) {
+ mWm = wm;
+ }
+
+ void enable(@NonNull String propertyName) {
+ setPropertyValue(propertyName, /* enabled */ true);
+ }
+
+ void disable(@NonNull String propertyName) {
+ setPropertyValue(propertyName, /* enabled */ false);
+ }
+
+ private void setPropertyValue(@NonNull String propertyName, boolean enabled) {
+ final PackageManager.Property property = new PackageManager.Property(propertyName,
+ /* value */ enabled, /* packageName */ "", /* className */ "");
+ final PackageManager pm = mWm.mContext.getPackageManager();
+ spyOn(pm);
+ try {
+ doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
+ } catch (PackageManager.NameNotFoundException e) {
+ fail(e.getLocalizedMessage());
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java
new file mode 100644
index 0000000..2ef77f6
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxConfigurationRobot.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.when;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Robot implementation for {@link LetterboxConfiguration}.
+ */
+class AppCompatLetterboxConfigurationRobot {
+
+ @NonNull
+ private final LetterboxConfiguration mLetterboxConfiguration;
+
+ AppCompatLetterboxConfigurationRobot(@NonNull LetterboxConfiguration letterboxConfiguration) {
+ mLetterboxConfiguration = letterboxConfiguration;
+ spyOn(mLetterboxConfiguration);
+ }
+
+ void enableTranslucentPolicy(boolean enabled) {
+ when(mLetterboxConfiguration.isTranslucentLetterboxingEnabled()).thenReturn(enabled);
+ }
+
+ void enablePolicyForIgnoringRequestedOrientation(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration)
+ .isPolicyForIgnoringRequestedOrientationEnabled();
+ }
+
+ void enableCameraCompatTreatment(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
+ }
+
+ void enableCameraCompatTreatmentAtBuildTime(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration)
+ .isCameraCompatTreatmentEnabledAtBuildTime();
+ }
+
+ void enableUserAppAspectRatioFullscreen(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration).isUserAppAspectRatioFullscreenEnabled();
+ }
+
+ void enableUserAppAspectRatioSettings(boolean enabled) {
+ doReturn(enabled).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
new file mode 100644
index 0000000..1720b64
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
+import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.res.Configuration;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.wm.utils.CurrentTimeMillisSupplierFake;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+
+/**
+ * Test class for {@link AppCompatOrientationOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatOrientationOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatOrientationOverridesTest extends WindowTestsBase {
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_activityRelaunching_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ true,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.applyOnConf((c) -> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ c.enablePolicyForIgnoringRequestedOrientation(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.enableTreatmentForTopActivity(true);
+ });
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(false);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ true,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ public void testShouldIgnoreRequestedOrientation_overrideDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ false,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ public void testShouldIgnoreRequestedOrientation_propertyIsTrue_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.prop().enable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ true,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
+ public void testShouldIgnoreRequestedOrientation_propertyIsFalseAndOverride_returnsFalse()
+ throws Exception {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.prop().disable(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION);
+
+ robot.activity().createActivityWithComponent();
+ robot.prepareRelaunchingAfterRequestedOrientationChanged(true);
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ false,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.prop().disable(
+ PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(true);
+ });
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ i);
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.prepareMockedTime();
+ robot.checkRequestLoopExtended((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ 0);
+ robot.delay();
+ });
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
+ public void testShouldIgnoreOrientationRequestLoop_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.checkRequestLoop((i) -> {
+ robot.checkShouldNotIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ i);
+ });
+ robot.checkShouldIgnoreOrientationLoop();
+ robot.checkExpectedLoopCount(/* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
+ public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.conf().enablePolicyForIgnoringRequestedOrientation(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setLetterboxedForFixedOrientationAndAspectRatio(false);
+ });
+
+ robot.checkShouldIgnoreRequestedOrientation(/* expected */ false,
+ /* requestedOrientation */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<OrientationOverridesRobotTest> consumer) {
+ spyOn(mWm.mLetterboxConfiguration);
+ final OrientationOverridesRobotTest robot =
+ new OrientationOverridesRobotTest(mWm, mAtm, mSupervisor);
+ consumer.accept(robot);
+ }
+
+ private static class OrientationOverridesRobotTest extends AppCompatRobotBase {
+
+ @NonNull
+ private final CurrentTimeMillisSupplierFake mTestCurrentTimeMillisSupplier;
+
+ OrientationOverridesRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ super(wm, atm, supervisor);
+ mTestCurrentTimeMillisSupplier = new CurrentTimeMillisSupplierFake();
+ }
+
+ void prepareRelaunchingAfterRequestedOrientationChanged(boolean enabled) {
+ getTopOrientationOverrides().setRelaunchingAfterRequestedOrientationChanged(enabled);
+ }
+
+ // Useful to reduce timeout during tests
+ void prepareMockedTime() {
+ getTopOrientationOverrides().mOrientationOverridesState.mCurrentTimeMillisSupplier =
+ mTestCurrentTimeMillisSupplier;
+ }
+
+ void delay() {
+ mTestCurrentTimeMillisSupplier.delay(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS);
+ }
+
+ void checkShouldIgnoreRequestedOrientation(boolean expected,
+ @Configuration.Orientation int requestedOrientation) {
+ assertEquals(expected, getTopOrientationOverrides()
+ .shouldIgnoreRequestedOrientation(requestedOrientation));
+ }
+
+ void checkExpectedLoopCount(int expectedCount) {
+ assertEquals(expectedCount, getTopOrientationOverrides()
+ .getSetOrientationRequestCounter());
+ }
+
+ void checkShouldNotIgnoreOrientationLoop() {
+ assertFalse(getTopOrientationOverrides().shouldIgnoreOrientationRequestLoop());
+ }
+
+ void checkShouldIgnoreOrientationLoop() {
+ assertTrue(getTopOrientationOverrides().shouldIgnoreOrientationRequestLoop());
+ }
+
+ void checkRequestLoop(IntConsumer consumer) {
+ for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
+ consumer.accept(i);
+ }
+ }
+
+ void checkRequestLoopExtended(IntConsumer consumer) {
+ for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
+ consumer.accept(i);
+ }
+ }
+
+ private AppCompatOrientationOverrides getTopOrientationOverrides() {
+ return activity().top().mAppCompatController.getAppCompatOverrides()
+ .getAppCompatOrientationOverrides();
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
new file mode 100644
index 0000000..9885a2d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
+import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
+import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
+import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.verify;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.pm.ActivityInfo;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatOrientationPolicy}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatOrientationPolicyTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatOrientationPolicyTest extends WindowTestsBase {
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ @Test
+ public void testOverrideOrientationIfNeeded_mapInvokedOnRequest() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.overrideOrientationIfNeeded(SCREEN_ORIENTATION_PORTRAIT);
+ robot.checkOrientationRequestMapped();
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUser() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.activity().setIgnoreOrientationRequest(true);
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_USER);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_optOut_isUnchanged() {
+ runTestScenario((robot) -> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutSystem_returnsUser() {
+ runTestScenario((robot) -> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+ robot.conf().enableUserAppAspectRatioFullscreen(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_USER);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutUser_returnsUser() {
+ runTestScenario((robot) -> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+ robot.conf().enableUserAppAspectRatioFullscreen(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_USER);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUnchanged()
+ throws Exception {
+ runTestScenarioWithActivity((robot) -> {
+ robot.activity().setIgnoreOrientationRequest(false);
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
+ public void testOverrideOrientationIfNeeded_fullscreenAndUserOverrideEnabled_isUnchanged() {
+ runTestScenario((robot) -> {
+ robot.conf().enableUserAppAspectRatioSettings(true);
+
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+ public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsPortrait() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
+ public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsNosensor() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_NOSENSOR);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
+ public void testOverrideOrientationIfNeeded_nosensorOverride_orientationFixed_isUnchanged() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
+ public void testOverrideOrientationIfNeeded_reverseLandscape_portraitOrUndefined_isUnchanged() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ robot.checkOverrideOrientation(
+ /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
+ public void testOverrideOrientationIfNeeded_reverseLandscape_Landscape_getsReverseLandscape() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_LANDSCAPE,
+ /* expected */ SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+ public void testOverrideOrientationIfNeeded_portraitOverride_orientationFixed_IsUnchanged() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_NOSENSOR,
+ /* expected */ SCREEN_ORIENTATION_NOSENSOR);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_portraitAndIgnoreFixedOverrides_returnsPortrait() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_NOSENSOR,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_noSensorAndIgnoreFixedOverrides_returnsNosensor() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_NOSENSOR);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
+ public void testOverrideOrientationIfNeeded_propertyIsFalse_isUnchanged()
+ throws Exception {
+ runTestScenario((robot) -> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+
+ robot.activity().createActivityWithComponent();
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
+ OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+ public void testOverrideOrientationIfNeeded_whenCameraNotActive_isUnchanged() {
+ runTestScenario((robot) -> {
+ robot.applyOnConf((c)-> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.setTopActivityEligibleForOrientationOverride(false);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
+ OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
+ public void testOverrideOrientationIfNeeded_whenCameraActive_returnsPortrait() {
+ runTestScenario((robot) -> {
+ robot.applyOnConf((c) -> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.setTopActivityEligibleForOrientationOverride(true);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(true);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_USER);
+ });
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_fullscreenOverride_cameraActivity_unchanged() {
+ runTestScenario((robot) -> {
+ robot.applyOnConf((c) -> {
+ c.enableCameraCompatTreatment(true);
+ c.enableCameraCompatTreatmentAtBuildTime(true);
+ });
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ a.setTopActivityCameraActive(false);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(false);
+ });
+
+ robot.checkOverrideOrientationIsNot(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* notExpected */ SCREEN_ORIENTATION_USER);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_userFullScreenOverrideOverSystem_returnsUser() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(true);
+ });
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_USER);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
+ public void testOverrideOrientationIfNeeded_respectOrientationReqOverUserFullScreenAndSystem() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.setShouldApplyUserFullscreenOverride(true);
+ a.setIgnoreOrientationRequest(false);
+ });
+ robot.checkOverrideOrientationIsNot(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* notExpected */ SCREEN_ORIENTATION_USER);
+ });
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_userFullScreenOverrideDisabled_returnsUnchanged() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.activity().setShouldApplyUserFullscreenOverride(false);
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_PORTRAIT,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ });
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_userAspectRatioApplied_unspecifiedOverridden() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.activity().setShouldApplyUserMinAspectRatioOverride(true);
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_LOCKED,
+ /* expected */ SCREEN_ORIENTATION_PORTRAIT);
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_LANDSCAPE,
+ /* expected */ SCREEN_ORIENTATION_LANDSCAPE);
+ });
+ }
+
+ @Test
+ public void testOverrideOrientationIfNeeded_userAspectRatioNotApplied_isUnchanged() {
+ runTestScenarioWithActivity((robot) -> {
+ robot.activity().setShouldApplyUserFullscreenOverride(false);
+
+ robot.checkOverrideOrientation(/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED,
+ /* expected */ SCREEN_ORIENTATION_UNSPECIFIED);
+ });
+ }
+
+
+ /**
+ * Runs a test scenario with an existing activity providing a Robot.
+ */
+ void runTestScenarioWithActivity(@NonNull Consumer<OrientationPolicyRobotTest> consumer) {
+ runTestScenario(/* withActivity */ true, consumer);
+ }
+
+ /**
+ * Runs a test scenario without an existing activity providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<OrientationPolicyRobotTest> consumer) {
+ runTestScenario(/* withActivity */ false, consumer);
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(boolean withActivity,
+ @NonNull Consumer<OrientationPolicyRobotTest> consumer) {
+ spyOn(mWm.mLetterboxConfiguration);
+ final OrientationPolicyRobotTest robot =
+ new OrientationPolicyRobotTest(mWm, mAtm, mSupervisor, withActivity);
+ consumer.accept(robot);
+ }
+
+ private static class OrientationPolicyRobotTest extends AppCompatRobotBase{
+
+ private final WindowManagerService mWm;
+
+ OrientationPolicyRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor,
+ boolean withActivity) {
+ super(wm, atm, supervisor);
+ mWm = wm;
+ spyOn(mWm);
+ if (withActivity) {
+ activity().createActivityWithComponent();
+ }
+ }
+
+ int overrideOrientationIfNeeded(@ActivityInfo.ScreenOrientation int candidate) {
+ return activity().top().mAppCompatController.getOrientationPolicy()
+ .overrideOrientationIfNeeded(candidate);
+ }
+
+ void checkOrientationRequestMapped() {
+ verify(mWm).mapOrientationRequest(SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ void checkOverrideOrientation(@ActivityInfo.ScreenOrientation int candidate,
+ @ActivityInfo.ScreenOrientation int expected) {
+ Assert.assertEquals(expected, overrideOrientationIfNeeded(candidate));
+ }
+
+ void checkOverrideOrientationIsNot(@ActivityInfo.ScreenOrientation int candidate,
+ @ActivityInfo.ScreenOrientation int notExpected) {
+ Assert.assertNotEquals(notExpected, overrideOrientationIfNeeded(candidate));
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
new file mode 100644
index 0000000..de16e38
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import androidx.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+/**
+ * Base class for the Robots related to App Compat tests.
+ */
+abstract class AppCompatRobotBase {
+
+ private static final int DEFAULT_DISPLAY_WIDTH = 1000;
+ private static final int DEFAULT_DISPLAY_HEIGHT = 2000;
+
+ @NonNull
+ private final AppCompatActivityRobot mActivityRobot;
+ @NonNull
+ private final AppCompatLetterboxConfigurationRobot mConfigurationRobot;
+ @NonNull
+ private final AppCompatComponentPropRobot mOptPropRobot;
+
+ AppCompatRobotBase(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor,
+ int displayWidth, int displayHeight) {
+ mActivityRobot = new AppCompatActivityRobot(wm, atm, supervisor,
+ displayWidth, displayHeight);
+ mConfigurationRobot =
+ new AppCompatLetterboxConfigurationRobot(wm.mLetterboxConfiguration);
+ mOptPropRobot = new AppCompatComponentPropRobot(wm);
+ }
+
+ AppCompatRobotBase(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ this(wm, atm, supervisor, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
+ }
+
+ @NonNull
+ AppCompatLetterboxConfigurationRobot conf() {
+ return mConfigurationRobot;
+ }
+
+ @NonNull
+ void applyOnConf(@NonNull Consumer<AppCompatLetterboxConfigurationRobot> consumer) {
+ consumer.accept(mConfigurationRobot);
+ }
+
+ @NonNull
+ AppCompatActivityRobot activity() {
+ return mActivityRobot;
+ }
+
+ @NonNull
+ void applyOnActivity(@NonNull Consumer<AppCompatActivityRobot> consumer) {
+ consumer.accept(mActivityRobot);
+ }
+
+ @NonNull
+ AppCompatComponentPropRobot prop() {
+ return mOptPropRobot;
+ }
+
+ @NonNull
+ void applyOnProp(@NonNull Consumer<AppCompatComponentPropRobot> consumer) {
+ consumer.accept(mOptPropRobot);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatTransparentActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatTransparentActivityRobot.java
new file mode 100644
index 0000000..3cfbb9e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatTransparentActivityRobot.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.WindowConfiguration;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.Assert;
+
+import java.util.function.Consumer;
+
+/**
+ * Robot implementation for {@link ActivityRecord} when dealing with transparent activities.
+ */
+class AppCompatTransparentActivityRobot {
+
+ @Nullable
+ private WindowConfiguration mTopActivityWindowConfiguration;
+
+ @NonNull
+ private final AppCompatActivityRobot mActivityRobot;
+
+ AppCompatTransparentActivityRobot(@NonNull AppCompatActivityRobot activityRobot) {
+ mActivityRobot = activityRobot;
+ }
+
+ @NonNull
+ AppCompatActivityRobot activity() {
+ return mActivityRobot;
+ }
+
+ @NonNull
+ void applyOnActivity(@NonNull Consumer<AppCompatActivityRobot> consumer) {
+ consumer.accept(mActivityRobot);
+ }
+
+ void launchTransparentActivity() {
+ mActivityRobot.launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
+ SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
+ /* withComponent */ false, /* addToTask */ false);
+ }
+
+ void launchTransparentActivityInTask() {
+ mActivityRobot.launchActivity(/*minAspectRatio */ -1,
+ /* maxAspectRatio */ -1, SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
+ /* withComponent */ false, /* addToTask */true);
+ }
+
+ void launchOpaqueActivityInTask() {
+ mActivityRobot.launchActivity(/*minAspectRatio */ -1,
+ /* maxAspectRatio */ -1, SCREEN_ORIENTATION_PORTRAIT, /* transparent */ false,
+ /* withComponent */ false, /* addToTask */true);
+ }
+
+ void forceChangeInTopActivityConfiguration() {
+ activity().applyToTopActivity((topActivity) -> {
+ final Configuration requestedConfig =
+ topActivity.getRequestedOverrideConfiguration();
+ mTopActivityWindowConfiguration = requestedConfig.windowConfiguration;
+ mTopActivityWindowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ mTopActivityWindowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ mTopActivityWindowConfiguration.setAlwaysOnTop(true);
+ topActivity.onRequestedOverrideConfigurationChanged(requestedConfig);
+ });
+ }
+
+ void checkTopActivityConfigurationConfiguration() {
+ activity().applyToTopActivity((topActivity) -> {
+ // The original override of WindowConfiguration should keep.
+ assertEquals(ACTIVITY_TYPE_STANDARD, topActivity.getActivityType());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW,
+ mTopActivityWindowConfiguration.getWindowingMode());
+ assertTrue(mTopActivityWindowConfiguration.isAlwaysOnTop());
+ // Unless display is going to be rotated, it should always inherit from parent.
+ assertEquals(ROTATION_UNDEFINED,
+ mTopActivityWindowConfiguration.getDisplayRotation());
+ });
+ }
+
+ void checkTopActivityTransparentPolicyStateIsRunning(boolean running) {
+ assertEquals(running,
+ activity().top().mAppCompatController.getTransparentPolicy().isRunning());
+ }
+
+ void checkTopActivityTransparentPolicyStartInvoked() {
+ activity().applyToTopActivity((topActivity) -> {
+ verify(topActivity.mAppCompatController.getTransparentPolicy()).start();
+ });
+ }
+
+ void checkTopActivityTransparentPolicyStartNotInvoked() {
+ verify(activity().top().mAppCompatController.getTransparentPolicy(), never()).start();
+ }
+
+ void checkTopActivityTransparentPolicyStopInvoked() {
+ verify(activity().top().mAppCompatController.getTransparentPolicy()).stop();
+ }
+
+ void checkTopActivityTransparentPolicyStopNotInvoked() {
+ verify(activity().top().mAppCompatController.getTransparentPolicy(), never()).stop();
+ }
+
+ void checkTopActivityHasInheritedBoundsFrom(int fromTop) {
+ final ActivityRecord topActivity = activity().top();
+ final ActivityRecord otherActivity = activity().getFromTop(/* fromTop */ fromTop);
+ final Rect opaqueBounds = otherActivity.getConfiguration().windowConfiguration
+ .getBounds();
+ final Rect translucentRequestedBounds = topActivity.getRequestedOverrideBounds();
+ Assert.assertEquals(opaqueBounds, translucentRequestedBounds);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index c67d1ec..63c14b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -30,6 +30,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
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.ActivityRecord.State.STOPPED;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -158,7 +159,7 @@
// reset drawing status to test translucent activity
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
final ActivityRecord topActivity = topTask.getTopMostActivity();
makeWindowVisibleAndDrawn(topActivity.findMainWindow());
// simulate translucent
@@ -169,7 +170,8 @@
// reset drawing status to test if previous task is translucent activity
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
+ makeWindowVisibleAndDrawn(topActivity.findMainWindow());
// simulate translucent
recordA.setOccludesParent(false);
backNavigationInfo = startBackNavigation();
@@ -180,7 +182,7 @@
topActivity.setOccludesParent(true);
recordA.setOccludesParent(true);
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(topActivity.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
@@ -188,7 +190,7 @@
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
doReturn(true).when(recordA).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -245,8 +247,9 @@
assertTrue("Animation scheduled", backNavigationInfo.isPrepareRemoteAnimation());
// reset drawing status
+ testCase.recordBack.setState(STOPPED, "stopped");
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
@@ -255,7 +258,7 @@
// reset drawing status, test if top activity is translucent
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordFront.findMainWindow());
// simulate translucent
testCase.recordFront.setOccludesParent(false);
@@ -266,7 +269,7 @@
// reset drawing status, test if bottom activity is translucent
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
makeWindowVisibleAndDrawn(testCase.recordBack.findMainWindow());
// simulate translucent
testCase.recordBack.setOccludesParent(false);
@@ -277,7 +280,7 @@
// reset drawing status, test canShowWhenLocked
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
doReturn(true).when(testCase.recordBack).canShowWhenLocked();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -480,7 +483,10 @@
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
backNavigationInfo.onBackNavigationFinished(false);
- mBackNavigationController.clearBackAnimations();
+ mBackNavigationController.clearBackAnimations(true);
+
+ final WindowState window = topTask.getTopVisibleAppMainWindow();
+ makeWindowVisibleAndDrawn(window);
setupKeyguardOccluded();
backNavigationInfo = startBackNavigation();
assertThat(typeToString(backNavigationInfo.getType()))
@@ -840,7 +846,7 @@
toHomeBuilder.build();
verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface(
any(), any(), any(), any(), any(), any());
- animationHandler.clearBackAnimateTarget();
+ animationHandler.clearBackAnimateTarget(true);
openActivities.clear();
// Back to ACTIVITY and TASK have the same logic, just with different target.
@@ -937,6 +943,7 @@
testCase.recordFront = record2;
testCase.windowBack = window1;
testCase.windowFront = window2;
+ record1.setState(STOPPED, "stopped");
return testCase;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index b21eca7..2bda950 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -19,7 +19,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -106,31 +105,7 @@
}
@Test
- public void testScheduleTransactionItem_notBundle() throws RemoteException {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem);
-
- verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
- ClientTransaction transaction = mTransactionCaptor.getValue();
- assertEquals(1, transaction.getCallbacks().size());
- assertEquals(mTransactionItem, transaction.getCallbacks().get(0));
- assertNull(transaction.getLifecycleStateRequest());
- assertNull(transaction.getTransactionItems());
-
- clearInvocations(mLifecycleManager);
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
-
- verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
- transaction = mTransactionCaptor.getValue();
- assertNull(transaction.getCallbacks());
- assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest());
- }
-
- @Test
public void testScheduleTransactionItem() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
@@ -176,23 +151,7 @@
}
@Test
- public void testScheduleTransactionAndLifecycleItems_notBundle() throws RemoteException {
- mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem,
- mLifecycleItem);
-
- verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture());
- final ClientTransaction transaction = mTransactionCaptor.getValue();
- assertEquals(1, transaction.getCallbacks().size());
- assertEquals(mTransactionItem, transaction.getCallbacks().get(0));
- assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest());
- }
-
- @Test
public void testScheduleTransactionAndLifecycleItems() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
@@ -216,7 +175,6 @@
@Test
public void testScheduleTransactionAndLifecycleItems_shouldDispatchImmediately()
throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
@@ -230,8 +188,6 @@
@Test
public void testDispatchPendingTransactions() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
mLifecycleManager.mPendingTransactions.put(mClientBinder, mTransaction);
mLifecycleManager.dispatchPendingTransactions();
@@ -243,7 +199,6 @@
@Test
public void testLayoutDeferred() throws RemoteException {
- mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
spyOn(mWms.mWindowPlacerLocked);
doReturn(false).when(mWms.mWindowPlacerLocked).isInLayout();
doReturn(false).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index d2494ff..fbc4c7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -172,14 +172,14 @@
@Test
public void testSurfaceOrigin_applied() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setPosition(mSurfaces.top, -1000, -2000);
}
@Test
public void testApplySurfaceChanges_setColor() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
@@ -187,7 +187,7 @@
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
}
@@ -195,7 +195,7 @@
@Test
public void testNeedsApplySurfaceChanges_wallpaperBackgroundRequested() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setAlpha(mSurfaces.top, 1.0f);
assertFalse(mLetterbox.needsApplySurfaceChanges());
@@ -204,14 +204,14 @@
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
}
@Test
public void testNeedsApplySurfaceChanges_setParentSurface() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).reparent(mSurfaces.top, mParentSurface);
assertFalse(mLetterbox.needsApplySurfaceChanges());
@@ -220,14 +220,14 @@
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).reparent(mSurfaces.top, mParentSurface);
}
@Test
public void testApplySurfaceChanges_cornersNotRounded_surfaceFullWindowSurfaceNotCreated() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertNull(mSurfaces.fullWindowSurface);
}
@@ -236,7 +236,7 @@
public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertNotNull(mSurfaces.fullWindowSurface);
}
@@ -245,7 +245,7 @@
public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
mHasWallpaperBackground = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertNotNull(mSurfaces.fullWindowSurface);
}
@@ -254,7 +254,7 @@
public void testNotIntersectsOrFullyContains_cornersRounded() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
assertTrue(mLetterbox.notIntersectsOrFullyContains(new Rect(1, 2, 9, 9)));
}
@@ -262,14 +262,19 @@
@Test
public void testSurfaceOrigin_changeCausesReapply() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
clearInvocations(mTransaction);
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
assertTrue(mLetterbox.needsApplySurfaceChanges());
- mLetterbox.applySurfaceChanges(mTransaction);
+ applySurfaceChanges();
verify(mTransaction).setPosition(mSurfaces.top, 0, 0);
}
+ private void applySurfaceChanges() {
+ mLetterbox.applySurfaceChanges(/* syncTransaction */ mTransaction,
+ /* pendingTransaction */ mTransaction);
+ }
+
class SurfaceControlMocker implements Supplier<SurfaceControl.Builder> {
private SurfaceControl.Builder mLeftBuilder;
public SurfaceControl left;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index cd19449..bdd45c6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -18,29 +18,14 @@
import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
-import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
-import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
-import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
-import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -50,21 +35,16 @@
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
-import static android.view.WindowManager.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION;
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.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.LetterboxUiController.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
-import static com.android.server.wm.LetterboxUiController.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
import static org.junit.Assert.assertEquals;
@@ -149,175 +129,6 @@
mController = new LetterboxUiController(mWm, mActivity);
}
- // shouldIgnoreRequestedOrientation
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
- public void testShouldIgnoreRequestedOrientation_activityRelaunching_returnsTrue() {
- prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
-
- assertTrue(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
- public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() {
- doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
- doReturn(true).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
-
- // Recreate DisplayContent with DisplayRotationCompatPolicy
- mActivity = setUpActivityWithComponent();
- mController = new LetterboxUiController(mWm, mActivity);
- prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
- mController.setRelaunchingAfterRequestedOrientationChanged(false);
-
- spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
- doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
- .isTreatmentEnabledForActivity(eq(mActivity));
-
- assertTrue(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- public void testShouldIgnoreRequestedOrientation_overrideDisabled_returnsFalse() {
- prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
-
- assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- public void testShouldIgnoreRequestedOrientation_propertyIsTrue_returnsTrue()
- throws Exception {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- mockThatProperty(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION, /* value */ true);
- mController = new LetterboxUiController(mWm, mActivity);
- prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
-
- assertTrue(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION})
- public void testShouldIgnoreRequestedOrientation_propertyIsFalseAndOverride_returnsFalse()
- throws Exception {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- mockThatProperty(PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION, /* value */ false);
-
- mController = new LetterboxUiController(mWm, mActivity);
- prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
-
- assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- public void testShouldIgnoreOrientationRequestLoop_overrideDisabled_returnsFalse() {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
- // Request 3 times to simulate orientation request loop
- for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
- assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
- /* expectedCount */ 0);
- }
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_propertyIsFalseAndOverride_returnsFalse()
- throws Exception {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- mockThatProperty(PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED,
- /* value */ false);
- doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
-
- mController = new LetterboxUiController(mWm, mActivity);
-
- // Request 3 times to simulate orientation request loop
- for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
- assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
- /* expectedCount */ 0);
- }
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_isLetterboxed_returnsFalse() {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- doReturn(true).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
-
- // Request 3 times to simulate orientation request loop
- for (int i = 0; i <= MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
- assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
- /* expectedCount */ i);
- }
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_noLoop_returnsFalse() {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
-
- // No orientation request loop
- assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
- /* expectedCount */ 0);
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_timeout_returnsFalse()
- throws InterruptedException {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
-
- for (int i = MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i > 0; i--) {
- assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
- /* expectedCount */ 0);
- Thread.sleep(SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS);
- }
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED})
- public void testShouldIgnoreOrientationRequestLoop_returnsTrue() {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- doReturn(false).when(mActivity).isLetterboxedForFixedOrientationAndAspectRatio();
-
- for (int i = 0; i < MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP; i++) {
- assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ false,
- /* expectedCount */ i);
- }
- assertShouldIgnoreOrientationRequestLoop(/* shouldIgnore */ true,
- /* expectedCount */ MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP);
- }
-
- private void assertShouldIgnoreOrientationRequestLoop(boolean shouldIgnore, int expectedCount) {
- if (shouldIgnore) {
- assertTrue(mController.shouldIgnoreOrientationRequestLoop());
- } else {
- assertFalse(mController.shouldIgnoreOrientationRequestLoop());
- }
- assertEquals(expectedCount, mController.getSetOrientationRequestCounter());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH})
- public void testShouldIgnoreRequestedOrientation_flagIsDisabled_returnsFalse() {
- prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch();
- doReturn(false).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
-
- assertFalse(mController.shouldIgnoreRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
// shouldRefreshActivityForCameraCompat
@Test
@@ -522,8 +333,9 @@
final Rect opaqueBounds = new Rect(0, 0, 500, 300);
doReturn(opaqueBounds).when(mActivity).getBounds();
// Activity is translucent
- spyOn(mActivity.mTransparentPolicy);
- when(mActivity.mTransparentPolicy.isRunning()).thenReturn(true);
+ spyOn(mActivity.mAppCompatController.getTransparentPolicy());
+ when(mActivity.mAppCompatController.getTransparentPolicy()
+ .isRunning()).thenReturn(true);
// Makes requested sizes different
mainWindow.mRequestedWidth = opaqueBounds.width() - 1;
@@ -709,311 +521,6 @@
return mainWindow;
}
- // overrideOrientationIfNeeded
-
- @Test
- public void testOverrideOrientationIfNeeded_mapInvokedOnRequest() throws Exception {
- mController = new LetterboxUiController(mWm, mActivity);
- spyOn(mWm);
-
- mController.overrideOrientationIfNeeded(SCREEN_ORIENTATION_PORTRAIT);
-
- verify(mWm).mapOrientationRequest(SCREEN_ORIENTATION_PORTRAIT);
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
- public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUser()
- throws Exception {
- mDisplayContent.setIgnoreOrientationRequest(true);
- assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
- public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_optOut_returnsUnchanged()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
-
- mController = new LetterboxUiController(mWm, mActivity);
- mDisplayContent.setIgnoreOrientationRequest(true);
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
- public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutSystem_returnsUser()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
- prepareActivityThatShouldApplyUserFullscreenOverride();
-
- // fullscreen override still applied
- assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
- public void testOverrideOrientationIfNeeded_fullscreenOverrides_optOutUser_returnsUser()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
- /* value */ false);
- prepareActivityThatShouldApplyUserFullscreenOverride();
-
- // fullscreen override still applied
- assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
- public void testOverrideOrientationIfNeeded_fullscreenOverrideEnabled_returnsUnchanged()
- throws Exception {
- mDisplayContent.setIgnoreOrientationRequest(false);
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_ANY_ORIENTATION_TO_USER})
- public void testOverrideOrientationIfNeeded_fullscreenAndUserOverrideEnabled_returnsUnchanged()
- throws Exception {
- prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
- public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsPortrait()
- throws Exception {
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
- public void testOverrideOrientationIfNeeded_portraitOverrideEnabled_returnsNosensor() {
- assertEquals(SCREEN_ORIENTATION_NOSENSOR, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR})
- public void testOverrideOrientationIfNeeded_nosensorOverride_orientationFixed_returnsUnchanged() {
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
- public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationPortraitOrUndefined_returnsUnchanged() {
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE})
- public void testOverrideOrientationIfNeeded_reverseLandscapeOverride_orientationLandscape_returnsReverseLandscape() {
- assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_LANDSCAPE));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
- public void testOverrideOrientationIfNeeded_portraitOverride_orientationFixed_returnsUnchanged() {
- assertEquals(SCREEN_ORIENTATION_NOSENSOR, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_NOSENSOR));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
- public void testOverrideOrientationIfNeeded_portraitAndIgnoreFixedOverrides_returnsPortrait() {
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_NOSENSOR));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR, OVERRIDE_ANY_ORIENTATION})
- public void testOverrideOrientationIfNeeded_noSensorAndIgnoreFixedOverrides_returnsNosensor() {
- assertEquals(SCREEN_ORIENTATION_NOSENSOR, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT})
- public void testOverrideOrientationIfNeeded_propertyIsFalse_returnsUnchanged()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, /* value */ false);
-
- mController = new LetterboxUiController(mWm, mActivity);
-
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
- OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
- public void testOverrideOrientationIfNeeded_whenCameraNotActive_returnsUnchanged() {
- doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
- doReturn(true).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
-
- // Recreate DisplayContent with DisplayRotationCompatPolicy
- mActivity = setUpActivityWithComponent();
- mController = new LetterboxUiController(mWm, mActivity);
-
- spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
- doReturn(false).when(mDisplayContent.mDisplayRotationCompatPolicy)
- .isActivityEligibleForOrientationOverride(eq(mActivity));
-
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT,
- OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA})
- public void testOverrideOrientationIfNeeded_whenCameraActive_returnsPortrait() {
- doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
- doReturn(true).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
-
- // Recreate DisplayContent with DisplayRotationCompatPolicy
- mActivity = setUpActivityWithComponent();
- mController = new LetterboxUiController(mWm, mActivity);
-
- spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
- doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
- .isActivityEligibleForOrientationOverride(eq(mActivity));
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- public void testOverrideOrientationIfNeeded_userFullscreenOverride_returnsUser() {
- spyOn(mController);
- doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
- mDisplayContent.setIgnoreOrientationRequest(true);
-
- assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- public void testOverrideOrientationIfNeeded_userFullscreenOverride_cameraActivity_noChange() {
- doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
- doReturn(true).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
-
- // Recreate DisplayContent with DisplayRotationCompatPolicy
- mActivity = setUpActivityWithComponent();
- mController = new LetterboxUiController(mWm, mActivity);
- spyOn(mController);
- doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
-
- spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
- doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
- .isCameraActive(mActivity, /* mustBeFullscreen= */ true);
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- public void testOverrideOrientationIfNeeded_systemFullscreenOverride_cameraActivity_noChange() {
- doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
- doReturn(true).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabledAtBuildTime();
-
- // Recreate DisplayContent with DisplayRotationCompatPolicy
- mActivity = setUpActivityWithComponent();
- mController = new LetterboxUiController(mWm, mActivity);
- spyOn(mController);
- doReturn(true).when(mController).isSystemOverrideToFullscreenEnabled();
-
- spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
- doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
- .isCameraActive(mActivity, /* mustBeFullscreen= */ true);
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
- spyOn(mController);
- doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
- mDisplayContent.setIgnoreOrientationRequest(false);
-
- assertNotEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
- public void testOverrideOrientationIfNeeded_userFullScreenOverrideOverSystem_returnsUser() {
- spyOn(mController);
- doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
- mDisplayContent.setIgnoreOrientationRequest(true);
-
- assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ANY_ORIENTATION})
- public void testOverrideOrientationIfNeeded_respectOrientationReqOverUserFullScreenAndSystem() {
- spyOn(mController);
- doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
- mDisplayContent.setIgnoreOrientationRequest(false);
-
- assertNotEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- public void testOverrideOrientationIfNeeded_userFullScreenOverrideDisabled_returnsUnchanged() {
- spyOn(mController);
- doReturn(false).when(mController).shouldApplyUserFullscreenOverride();
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
- }
-
- @Test
- public void testOverrideOrientationIfNeeded_userAspectRatioApplied_unspecifiedOverridden() {
- spyOn(mController);
- doReturn(true).when(mController).shouldApplyUserMinAspectRatioOverride();
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
-
- assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_LOCKED));
-
- // unchanged if orientation is specified
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_LANDSCAPE));
- }
-
- @Test
- public void testOverrideOrientationIfNeeded_userAspectRatioNotApplied_returnsUnchanged() {
- spyOn(mController);
- doReturn(false).when(mController).shouldApplyUserMinAspectRatioOverride();
-
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
- }
-
// shouldApplyUser...Override
@Test
public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() throws Exception {
@@ -1062,6 +569,7 @@
prepareActivityThatShouldApplyUserMinAspectRatioOverride();
mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
+ mActivity = setUpActivityWithComponent();
mController = new LetterboxUiController(mWm, mActivity);
assertFalse(mController.shouldEnableUserAspectRatioSettings());
@@ -1344,6 +852,8 @@
public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse()
throws Exception {
mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
+
+ mActivity = setUpActivityWithComponent();
mController = new LetterboxUiController(mWm, mActivity);
assertFalse(mController.shouldOverrideMinAspectRatio());
@@ -1472,6 +982,8 @@
public void testshouldOverrideForceResizeApp_propertyFalse_overrideEnabled_returnsFalse()
throws Exception {
mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
+
+ mActivity = setUpActivityWithComponent();
mController = new LetterboxUiController(mWm, mActivity);
assertFalse(mController.shouldOverrideForceResizeApp());
@@ -1528,6 +1040,8 @@
public void testshouldOverrideForceNonResizeApp_propertyFalse_overrideEnabled_returnsFalse()
throws Exception {
mockThatProperty(PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, /* value */ false);
+
+ mActivity = setUpActivityWithComponent();
mController = new LetterboxUiController(mWm, mActivity);
assertFalse(mController.shouldOverrideForceNonResizeApp());
@@ -1684,12 +1198,6 @@
mDisplayContent.setIgnoreOrientationRequest(true);
}
- private void prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch() {
- doReturn(true).when(mLetterboxConfiguration)
- .isPolicyForIgnoringRequestedOrientationEnabled();
- mController.setRelaunchingAfterRequestedOrientationChanged(true);
- }
-
private ActivityRecord setUpActivityWithComponent() {
mDisplayContent = new TestDisplayContent
.Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index ac1aa20..64527cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1315,6 +1315,47 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testOverrideMinAspectRatioSmall_overridden() {
+ final int dh = 1200;
+ final int dw = 1000;
+ setUpDisplaySizeWithApp(dw, dh);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .build();
+
+ final Rect bounds = activity.getBounds();
+ assertEquals(dh, bounds.height());
+ assertEquals(dh / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE,
+ bounds.width(), 0.5f);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+ public void testOverrideMinAspectRatioSmall_notOverridden() {
+ final int dh = 1200;
+ final int dw = 1000;
+ setUpDisplaySizeWithApp(dw, dh);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
+ .build();
+
+ // Activity's requested aspect ratio is larger than OVERRIDE_MIN_ASPECT_RATIO_SMALL,
+ // so OVERRIDE_MIN_ASPECT_RATIO_SMALL is ignored.
+ final Rect bounds = activity.getBounds();
+ assertEquals(dh, bounds.height());
+ assertEquals(dh / OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ bounds.width(), 0.5f);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioMedium() {
setUpDisplaySizeWithApp(1000, 1200);
@@ -4271,6 +4312,27 @@
}
+ @Test
+ public void testInsetOverrideNotAppliedInFreeform() {
+ final int notchHeight = 100;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
+ .setNotch(notchHeight)
+ .build();
+ setUpApp(display);
+
+ // Simulate inset override for legacy app bound behaviour
+ mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
+ // Set task as freeform
+ mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
+ // App bounds should not include insets and should match bounds when in freeform.
+ assertEquals(new Rect(0, 0, 1000, 2800), appBounds);
+ assertEquals(new Rect(0, 0, 1000, 2800), bounds);
+ }
+
private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox,
Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 6c5f975..1c32980 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -417,6 +418,22 @@
}
@Test
+ public void testSkipPrepareSync() {
+ final TestWindowContainer wc = new TestWindowContainer(mWm, true /* waiter */);
+ wc.mSkipPrepareSync = true;
+ final BLASTSyncEngine bse = createTestBLASTSyncEngine();
+ final BLASTSyncEngine.SyncGroup syncGroup = bse.prepareSyncSet(
+ mock(BLASTSyncEngine.TransactionReadyListener.class), "test");
+ bse.startSyncSet(syncGroup);
+ bse.addToSyncSet(syncGroup.mSyncId, wc);
+ assertEquals(SYNC_STATE_NONE, wc.mSyncState);
+ // If the implementation of prepareSync doesn't set sync state, the sync group should also
+ // be empty.
+ assertNull(wc.mSyncGroup);
+ assertTrue(wc.isSyncFinished(syncGroup));
+ }
+
+ @Test
public void testNonBlastMethod() {
mAppWindow = createWindow(null, TYPE_BASE_APPLICATION, "mAppWindow");
@@ -694,6 +711,7 @@
final boolean mWaiter;
boolean mVisibleRequested = true;
boolean mFillsParent = false;
+ boolean mSkipPrepareSync = false;
TestWindowContainer(WindowManagerService wms, boolean waiter) {
super(wms);
@@ -703,6 +721,9 @@
@Override
boolean prepareSync() {
+ if (mSkipPrepareSync) {
+ return false;
+ }
if (!super.prepareSync()) {
return false;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 9670a9a..a71b81e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -26,8 +26,8 @@
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
@@ -220,30 +220,7 @@
}
@Test
- public void testOnTaskFragmentAppeared_throughTaskFragmentOrganizer() throws RemoteException {
- mSetFlagsRule.disableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
-
- // No-op when the TaskFragment is not attached.
- mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
- mController.dispatchPendingEvents();
-
- verify(mOrganizer, never()).onTransactionReady(any());
- verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
-
- // Send callback when the TaskFragment is attached.
- setupMockParent(mTaskFragment, mTask);
-
- mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
- mController.dispatchPendingEvents();
-
- assertTaskFragmentParentInfoChangedTransaction(mTask);
- assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
- verify(mAppThread, never()).scheduleTaskFragmentTransaction(any(), any());
- }
-
- @Test
public void testOnTaskFragmentAppeared_throughApplicationThread() throws RemoteException {
- mSetFlagsRule.enableFlags(Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
// Re-register the organizer in case the flag was disabled during setup.
mController.unregisterOrganizer(mIOrganizer);
registerTaskFragmentOrganizer(mIOrganizer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index f94e5e3..35b6b70 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -499,8 +499,8 @@
.build();
final ActivityRecord activity0 = organizedTf.getBottomMostActivity();
final ActivityRecord activity1 = organizedTf.getTopMostActivity();
- // Bottom activity is untrusted embedding. Top activity is trusted embedded.
- // Activity0 has overlay over untrusted mode embedded.
+ // Bottom activity is untrusted embedding. Top activity is trusted embedded and occludes
+ // the bottom activity. Activity0 has overlay over untrusted mode embedded.
activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1;
activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID;
doReturn(true).when(organizedTf).isAllowedToEmbedActivityInUntrustedMode(activity0);
@@ -537,6 +537,48 @@
}
@Test
+ public void testActivityHasOverlayOverUntrustedModeEmbeddedWithAdjacentTaskFragments() {
+ final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
+ ACTIVITY_TYPE_STANDARD);
+ final Rect taskBounds = rootTask.getBounds();
+ final TaskFragment organizedTf0 = new TaskFragmentBuilder(mAtm)
+ .createActivityCount(1)
+ .setParentTask(rootTask)
+ .setFragmentToken(new Binder())
+ .setOrganizer(mOrganizer)
+ .setBounds(new Rect(taskBounds.left, taskBounds.top,
+ taskBounds.left + taskBounds.width() / 2, taskBounds.bottom))
+ .build();
+ final TaskFragment organizedTf1 = new TaskFragmentBuilder(mAtm)
+ .createActivityCount(1)
+ .setParentTask(rootTask)
+ .setFragmentToken(new Binder())
+ .setOrganizer(mOrganizer)
+ .setBounds(new Rect(taskBounds.left + taskBounds.width() / 2, taskBounds.top,
+ taskBounds.right, taskBounds.bottom))
+ .build();
+ final ActivityRecord activity0 = organizedTf0.getTopMostActivity();
+ final ActivityRecord activity1 = organizedTf1.getTopMostActivity();
+
+ activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1;
+ activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID;
+ doReturn(true).when(organizedTf0).isAllowedToEmbedActivityInUntrustedMode(activity0);
+
+ assertFalse("Activity0 doesn't have overlay because it's not occluded by activity1",
+ activity0.hasOverlayOverUntrustedModeEmbedded());
+ assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded());
+
+ // Expand organizedTf1 bounds slightly.
+ final Rect tfBounds1 = organizedTf1.getBounds();
+ organizedTf1.setBounds(tfBounds1.left - 5, tfBounds1.top, taskBounds.right + 5,
+ tfBounds1.bottom);
+
+ assertTrue("Activity0 has overlay because it's occluded partially by activity1",
+ activity0.hasOverlayOverUntrustedModeEmbedded());
+ assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded());
+ }
+
+ @Test
public void testIsAllowedToBeEmbeddedInTrustedMode() {
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index 1d6e307..f07b402 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -16,45 +16,22 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.app.WindowConfiguration;
-import android.content.res.Configuration;
-import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import org.junit.Assert;
+import androidx.annotation.NonNull;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.List;
import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
/**
* Test class for {@link TransparentPolicy}.
@@ -69,199 +46,238 @@
@Test
public void testNotStartingWhenDisabled() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
}, /* policyEnabled */ false);
}
@Test
public void testNotStartingWithoutTask() {
runTestScenario((robot) -> {
- robot.launchTransparentActivity();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivity();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testPolicyRunningWhenTransparentIsUsed() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsRunning();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ });
});
}
@Test
public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsRunning();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
- robot.clearInteractions();
- robot.destroyTopActivity();
+ robot.clearInteractions();
+ ta.activity().destroyTopActivity();
- robot.checkTopActivityPolicyStopInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStopInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
runTestScenario((robot) -> {
- robot.launchOpaqueActivityInTask();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ robot.transparentActivity((ta) -> {
+ ta.launchOpaqueActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
- robot.destroyActivity(/* fromTop */ 1);
- robot.checkTopActivityPolicyStartInvoked();
+ ta.activity().destroyActivity(/* fromTop */ 1);
+ ta.checkTopActivityTransparentPolicyStartInvoked();
+ });
});
}
@Test
public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
- robot.clearInteractions();
- robot.destroyActivity(/* fromTop */ 1);
+ robot.clearInteractions();
+ ta.activity().destroyActivity(/* fromTop */ 1);
- robot.checkTopActivityPolicyStartInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStartInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
runTestScenario((robot) -> {
- robot.launchOpaqueActivityInTask();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ robot.transparentActivity((ta) -> {
+ ta.launchOpaqueActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
- robot.clearInteractions();
- robot.checkTopActivityPolicyStopNotInvoked();
+ robot.clearInteractions();
+ ta.checkTopActivityTransparentPolicyStopNotInvoked();
+ });
});
}
@Test
public void testApplyStrategyToTranslucentActivities() {
runTestScenario((robot) -> {
- robot.configureTopActivity(/* minAspect */ 1.2f, /* maxAspect */ 1.5f,
- SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ true);
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.launchActivity(/* minAspect */ 1.1f, /* maxAspect */ 3f,
- SCREEN_ORIENTATION_LANDSCAPE, /* transparent */true, /* addToTask */true);
- robot.checkTopActivityPolicyStateIsRunning();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
- robot.checkTopOrientation(SCREEN_ORIENTATION_PORTRAIT);
- robot.checkTopAspectRatios(/* minAspectRatio */ 1.2f, /* maxAspectRatio */ 1.5f);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureTopActivity(/* minAspect */ 1.2f, /* maxAspect */ 1.5f,
+ SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ true);
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ a.launchActivity(/* minAspect */ 1.1f, /* maxAspect */ 3f,
+ SCREEN_ORIENTATION_LANDSCAPE, /* transparent */true,
+ /* withComponent */ false, /* addToTask */true);
+ });
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+ ta.applyOnActivity((a) -> {
+ a.checkTopActivityConfigOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ a.checkTopActivityAspectRatios(/* minAspectRatio */ 1.2f,
+ /* maxAspectRatio */ 1.5f);
+ });
+ });
});
}
@Test
public void testApplyStrategyToTransparentActivitiesRetainsWindowConfigurationProperties() {
runTestScenario((robot) -> {
- robot.launchTransparentActivity();
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivity();
- robot.forceChangeInTopActivityConfiguration();
- robot.attachTopActivityToTask();
+ ta.forceChangeInTopActivityConfiguration();
+ ta.activity().attachTopActivityToTask();
- robot.checkTopActivityConfigurationConfiguration();
+ ta.checkTopActivityConfigurationConfiguration();
+ });
});
}
@Test
public void testApplyStrategyToMultipleTranslucentActivities() {
runTestScenario((robot) -> {
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+ robot.transparentActivity((ta) -> {
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStateIsRunning();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 2);
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 2);
+ });
});
}
@Test
public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
runTestScenario((robot) -> {
- robot.configureTopActivityAsEmbedded();
- robot.launchTransparentActivityInTask();
+ robot.transparentActivity((ta) -> {
+ ta.activity().setTopActivityAsEmbedded(true);
+ ta.launchTransparentActivityInTask();
- robot.checkTopActivityPolicyStartNotInvoked();
- robot.checkTopActivityPolicyStateIsNotRunning();
+ ta.checkTopActivityTransparentPolicyStartNotInvoked();
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
}
@Test
public void testTranslucentActivitiesDontGoInSizeCompatMode() {
runTestScenario((robot) -> {
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- robot.rotateDisplayForTopActivity(ROTATION_90);
- robot.checkTopActivitySizeCompatMode(/* inScm */ true);
- robot.rotateDisplayForTopActivity(ROTATION_0);
- robot.checkTopActivitySizeCompatMode(/* inScm */ false);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.rotateDisplayForTopActivity(ROTATION_90);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ true);
+ a.rotateDisplayForTopActivity(ROTATION_0);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivitySizeCompatMode(/* inScm */ false);
- robot.rotateDisplayForTopActivity(ROTATION_90);
- robot.checkTopActivitySizeCompatMode(/* inScm */ false);
+ ta.launchTransparentActivityInTask();
+
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+ a.rotateDisplayForTopActivity(ROTATION_90);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+ });
+ });
}, /* displayWidth */ 2800, /* displayHeight */ 1400);
}
@Test
public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() {
runTestScenario((robot) -> {
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.launchTransparentActivity();
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ ta.launchTransparentActivity();
- robot.assertFalseOnTopActivity(ActivityRecord::fillsParent);
- robot.assertTrueOnActivity(/* fromTop */ 1, ActivityRecord::fillsParent);
- robot.applyTo(/* fromTop */ 1, (activity) -> {
- activity.finishing = true;
+ a.assertFalseOnTopActivity(ActivityRecord::fillsParent);
+ a.assertTrueOnActivity(/* fromTop */ 1, ActivityRecord::fillsParent);
+ a.applyToActivity(/* fromTop */ 1, (activity) -> {
+ activity.finishing = true;
+ });
+ a.assertFalseOnActivity(/* fromTop */ 1, ActivityRecord::occludesParent);
+ a.attachTopActivityToTask();
+
+ ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+ });
});
- robot.assertFalseOnActivity(/* fromTop */ 1, ActivityRecord::occludesParent);
- robot.attachTopActivityToTask();
-
- robot.checkTopActivityPolicyStateIsNotRunning();
});
}
@Test
public void testTranslucentActivitiesWhenUnfolding() {
runTestScenario((robot) -> {
- robot.applyToTop((activity) -> {
- activity.mWmService.mLetterboxConfiguration
- .setLetterboxHorizontalPositionMultiplier(1.0f);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.applyToTopActivity((topActivity) -> {
+ topActivity.mWmService.mLetterboxConfiguration
+ .setLetterboxHorizontalPositionMultiplier(1.0f);
+ });
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ ta.launchTransparentActivityInTask();
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+
+ a.setTaskWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ a.configureTopActivityFoldablePosture(/* isHalfFolded */ true,
+ /* isTabletop */ false);
+ a.checkTopActivityRecomputedConfiguration();
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+ robot.clearInteractions();
+ a.configureTopActivityFoldablePosture(/* isHalfFolded */ false,
+ /* isTabletop */ false);
+ a.checkTopActivityRecomputedConfiguration();
+ ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+ });
});
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.launchTransparentActivityInTask();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
-
- robot.configureTaskWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- robot.configureTopActivityFoldablePosture(/* isHalfFolded */ true,
- /* isTabletop */ false);
- robot.checkTopActivityRecomputedConfiguration();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
- robot.clearInteractions();
-
- robot.configureTopActivityFoldablePosture(/* isHalfFolded */ false,
- /* isTabletop */ false);
- robot.checkTopActivityRecomputedConfiguration();
- robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
}, /* displayWidth */ 2800, /* displayHeight */ 1400);
}
@@ -269,31 +285,28 @@
@Test
public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() {
runTestScenario((robot) -> {
- robot.configureTopActivityIgnoreOrientationRequest(true);
- robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
- // Rotate to put activity in size compat mode.
- robot.rotateDisplayForTopActivity(ROTATION_90);
- robot.checkTopActivitySizeCompatMode(/* inScm */ true);
+ robot.transparentActivity((ta) -> {
+ ta.applyOnActivity((a) -> {
+ a.configureTopActivityIgnoreOrientationRequest(true);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+ // Rotate to put activity in size compat mode.
+ a.rotateDisplayForTopActivity(ROTATION_90);
+ a.checkTopActivityInSizeCompatMode(/* inScm */ true);
- robot.launchTransparentActivityInTask();
- robot.assertNotNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
- robot.applyToTop(ActivityRecord::clearSizeCompatMode);
- robot.assertNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+ ta.launchTransparentActivityInTask();
+ a.assertNotNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+ a.applyToTopActivity(ActivityRecord::clearSizeCompatMode);
+ a.assertNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+ });
+ });
});
}
private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer,
boolean policyEnabled, int displayWidth, int displayHeight) {
- spyOn(mWm.mLetterboxConfiguration);
- when(mWm.mLetterboxConfiguration.isTranslucentLetterboxingEnabled())
- .thenReturn(policyEnabled);
- final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm,
- displayWidth, displayHeight);
- final Task task = new TaskBuilder(mSupervisor).setDisplay(builder.build())
- .setCreateActivity(true).build();
- final ActivityRecord opaqueActivity = task.getTopNonFinishingActivity();
- final TransparentPolicyRobotTest robot = new TransparentPolicyRobotTest(mAtm, task,
- opaqueActivity);
+ final TransparentPolicyRobotTest robot =
+ new TransparentPolicyRobotTest(mWm, mAtm, mSupervisor, displayWidth, displayHeight);
+ robot.conf().enableTranslucentPolicy(policyEnabled);
consumer.accept(robot);
}
@@ -314,324 +327,31 @@
/**
* Robot pattern implementation for TransparentPolicy
- * TODO(b/344587983): Extract Robot to be reused in different test classes.
*/
- private static class TransparentPolicyRobotTest {
+ private static class TransparentPolicyRobotTest extends AppCompatRobotBase {
- private final ActivityTaskManagerService mAtm;
+ @NonNull
+ private final AppCompatTransparentActivityRobot mTransparentActivityRobot;
- private final Task mTask;
-
- private final ActivityStackTest mActivityStack;
-
- private WindowConfiguration mTopActivityWindowConfiguration;
-
- private TransparentPolicyRobotTest(ActivityTaskManagerService atm, Task task,
- ActivityRecord opaqueActivity) {
- mAtm = atm;
- mTask = task;
- mActivityStack = new ActivityStackTest();
- mActivityStack.pushActivity(opaqueActivity);
- spyOn(opaqueActivity.mTransparentPolicy);
+ private TransparentPolicyRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor,
+ int displayWidth, int displayHeight) {
+ super(wm, atm, supervisor, displayWidth, displayHeight);
+ mTransparentActivityRobot = new AppCompatTransparentActivityRobot(activity());
+ // We always create at least an opaque activity in a Task
+ activity().createNewTaskWithBaseActivity();
}
- void configureTopActivityAsEmbedded() {
- final ActivityRecord topActivity = mActivityStack.top();
- spyOn(topActivity);
- doReturn(true).when(topActivity).isEmbedded();
- }
-
- private void launchActivity(float minAspectRatio, float maxAspectRatio,
- @Configuration.Orientation int orientation, boolean transparent,
- boolean addToTask) {
- final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm)
- .setScreenOrientation(orientation)
- .setLaunchedFromUid(mActivityStack.base().getUid());
- if (transparent) {
- activityBuilder.setActivityTheme(android.R.style.Theme_Translucent);
- }
- if (minAspectRatio >= 0) {
- activityBuilder.setMinAspectRatio(minAspectRatio);
- }
- if (maxAspectRatio >= 0) {
- activityBuilder.setMaxAspectRatio(maxAspectRatio);
- }
- final ActivityRecord newActivity = activityBuilder.build();
- if (addToTask) {
- mTask.addChild(newActivity);
- }
- spyOn(newActivity.mTransparentPolicy);
- mActivityStack.pushActivity(newActivity);
- }
-
- void attachTopActivityToTask() {
- mTask.addChild(mActivityStack.top());
- }
-
- void launchTransparentActivity() {
- launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
- SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
- /* addToTask */ false);
- }
-
- void launchTransparentActivityInTask() {
- launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
- SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
- /* addToTask */true);
- }
-
- void launchOpaqueActivityInTask() {
- launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
- SCREEN_ORIENTATION_PORTRAIT, /* transparent */ false,
- /* addToTask */true);
- }
-
- void destroyTopActivity() {
- mActivityStack.top().removeImmediately();
- }
-
- void destroyActivity(int fromTop) {
- mActivityStack.applyTo(/* fromTop */ fromTop, ActivityRecord::removeImmediately);
- }
-
- void forceChangeInTopActivityConfiguration() {
- mActivityStack.applyToTop((activity) -> {
- final Configuration requestedConfig = activity.getRequestedOverrideConfiguration();
- mTopActivityWindowConfiguration = requestedConfig.windowConfiguration;
- mTopActivityWindowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- mTopActivityWindowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- mTopActivityWindowConfiguration.setAlwaysOnTop(true);
- activity.onRequestedOverrideConfigurationChanged(requestedConfig);
- });
- }
-
- void checkTopActivityPolicyStateIsRunning() {
- assertTrue(mActivityStack.top().mTransparentPolicy.isRunning());
- }
-
- void checkTopActivityPolicyStateIsNotRunning() {
- assertFalse(mActivityStack.top().mTransparentPolicy.isRunning());
- }
-
- void checkTopActivityPolicyStopInvoked() {
- verify(mActivityStack.top().mTransparentPolicy).stop();
- }
-
- void checkTopActivityPolicyStopNotInvoked() {
- mActivityStack.applyToTop((activity) -> {
- verify(activity.mTransparentPolicy, never()).stop();
- });
- }
-
- void checkTopActivityPolicyStartInvoked() {
- mActivityStack.applyToTop((activity) -> {
- verify(activity.mTransparentPolicy).start();
- });
- }
-
- void checkTopActivityPolicyStartNotInvoked() {
- verify(mActivityStack.top().mTransparentPolicy, never()).start();
- }
-
- void assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
- mActivityStack.applyTo(fromTop, (activity) -> {
- Assert.assertTrue(predicate.test(activity));
- });
- }
-
- void assertFalseOnTopActivity(Predicate<ActivityRecord> predicate) {
- Assert.assertFalse(predicate.test(mActivityStack.top()));
- }
-
- void assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
- mActivityStack.applyTo(fromTop, (activity) -> {
- Assert.assertFalse(predicate.test(activity));
- });
- }
-
- void assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter) {
- Assert.assertNotNull(getter.apply(mActivityStack.top()));
- }
-
- void assertNullOnTopActivity(Function<ActivityRecord, Object> getter) {
- Assert.assertNull(getter.apply(mActivityStack.top()));
- }
-
- void checkTopActivityConfigurationConfiguration() {
- mActivityStack.applyToTop((activity) -> {
- // The original override of WindowConfiguration should keep.
- assertEquals(ACTIVITY_TYPE_STANDARD, activity.getActivityType());
- assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- mTopActivityWindowConfiguration.getWindowingMode());
- assertTrue(mTopActivityWindowConfiguration.isAlwaysOnTop());
- // Unless display is going to be rotated, it should always inherit from parent.
- assertEquals(ROTATION_UNDEFINED,
- mTopActivityWindowConfiguration.getDisplayRotation());
- });
- }
-
- void checkTopActivityHasInheritedBoundsFrom(int fromTop) {
- final ActivityRecord topActivity = mActivityStack.top();
- final ActivityRecord otherActivity = mActivityStack.getFromTop(/* fromTop */ fromTop);
- final Rect opaqueBounds = otherActivity.getConfiguration().windowConfiguration
- .getBounds();
- final Rect translucentRequestedBounds = topActivity.getRequestedOverrideBounds();
- Assert.assertEquals(opaqueBounds, translucentRequestedBounds);
- }
-
- void checkTopActivityRecomputedConfiguration() {
- verify(mActivityStack.top()).recomputeConfiguration();
- }
-
- void checkTopOrientation(int orientation) {
- Assert.assertEquals(orientation, mActivityStack.top()
- .getRequestedConfigurationOrientation());
- }
-
- void configureTaskWindowingMode(int windowingMode) {
- mTask.setWindowingMode(windowingMode);
- }
-
- void checkTopAspectRatios(float minAspectRatio, float maxAspectRatio) {
- final ActivityRecord topActivity = mActivityStack.top();
- Assert.assertEquals(minAspectRatio, topActivity.getMinAspectRatio(), 0.0001);
- Assert.assertEquals(maxAspectRatio, topActivity.getMaxAspectRatio(), 0.0001);
- }
-
- void checkTopActivitySizeCompatMode(boolean inScm) {
- Assert.assertEquals(inScm, mActivityStack.top().inSizeCompatMode());
+ void transparentActivity(@NonNull Consumer<AppCompatTransparentActivityRobot> consumer) {
+ consumer.accept(mTransparentActivityRobot);
}
void clearInteractions() {
- mActivityStack.applyToAll((activity) -> {
+ activity().applyToAllActivities((activity) -> {
clearInvocations(activity);
- clearInvocations(activity.mTransparentPolicy);
+ clearInvocations(activity.mAppCompatController.getTransparentPolicy());
});
}
-
- void configureTopActivity(float minAspect, float maxAspect, int screenOrientation,
- boolean isUnresizable) {
- prepareLimitedBounds(mActivityStack.top(), minAspect, maxAspect, screenOrientation,
- isUnresizable);
- }
-
- void configureTopActivityIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
- mActivityStack.top().mDisplayContent
- .setIgnoreOrientationRequest(ignoreOrientationRequest);
- }
-
- void configureUnresizableTopActivity(int screenOrientation) {
- configureTopActivity(-1, -1, screenOrientation, true);
- }
-
- void applyToTop(Consumer<ActivityRecord> consumer) {
- consumer.accept(mActivityStack.top());
- }
-
- void applyTo(int fromTop, Consumer<ActivityRecord> consumer) {
- mActivityStack.applyTo(fromTop, consumer);
- }
-
- void rotateDisplayForTopActivity(int rotation) {
- rotateDisplay(mActivityStack.top().mDisplayContent, rotation);
- }
-
- /**
- * Setups activity with restriction on its bounds, such as maxAspect, minAspect,
- * fixed orientation, and/or whether it is resizable.
- */
- void prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect,
- int screenOrientation, boolean isUnresizable) {
- activity.info.resizeMode = isUnresizable
- ? RESIZE_MODE_UNRESIZEABLE
- : RESIZE_MODE_RESIZEABLE;
- final Task task = activity.getTask();
- if (task != null) {
- // Update the Task resize value as activity will follow the task.
- task.mResizeMode = activity.info.resizeMode;
- task.getRootActivity().info.resizeMode = activity.info.resizeMode;
- }
- activity.setVisibleRequested(true);
- if (maxAspect >= 0) {
- activity.info.setMaxAspectRatio(maxAspect);
- }
- if (minAspect >= 0) {
- activity.info.setMinAspectRatio(minAspect);
- }
- if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
- activity.info.screenOrientation = screenOrientation;
- activity.setRequestedOrientation(screenOrientation);
- }
- // Make sure to use the provided configuration to construct the size compat fields.
- activity.clearSizeCompatMode();
- activity.ensureActivityConfiguration();
- // Make sure the display configuration reflects the change of activity.
- if (activity.mDisplayContent.updateOrientation()) {
- activity.mDisplayContent.sendNewConfiguration();
- }
- }
-
- void configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
- mActivityStack.applyToTop((activity) -> {
- final DisplayRotation r = activity.mDisplayContent.getDisplayRotation();
- doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
- doReturn(false).when(r)
- .isDeviceInPosture(any(DeviceStateController.DeviceState.class),
- anyBoolean());
- if (isHalfFolded) {
- doReturn(true).when(r)
- .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED,
- isTabletop);
- }
- activity.recomputeConfiguration();
- });
- }
-
- private static void rotateDisplay(DisplayContent display, int rotation) {
- final Configuration c = new Configuration();
- display.getDisplayRotation().setRotation(rotation);
- display.computeScreenConfiguration(c);
- display.onRequestedOverrideConfigurationChanged(c);
- }
-
- /**
- * Contains all the ActivityRecord launched in the test. This is different from what's in
- * the Task because activities are added here even if not added to tasks.
- */
- private static class ActivityStackTest {
- private final List<ActivityRecord> mActivities = new ArrayList<>();
-
- void pushActivity(ActivityRecord activityRecord) {
- mActivities.add(activityRecord);
- }
-
- void applyToTop(Consumer<ActivityRecord> consumer) {
- consumer.accept(top());
- }
-
- ActivityRecord getFromTop(int fromTop) {
- return mActivities.get(mActivities.size() - fromTop - 1);
- }
-
- ActivityRecord base() {
- return mActivities.get(0);
- }
-
- ActivityRecord top() {
- return mActivities.get(mActivities.size() - 1);
- }
-
- // Allows access to the activity at position beforeLast from the top.
- // If fromTop = 0 the activity used is the top one.
- void applyTo(int fromTop, Consumer<ActivityRecord> consumer) {
- consumer.accept(getFromTop(fromTop));
- }
-
- void applyToAll(Consumer<ActivityRecord> consumer) {
- for (int i = mActivities.size() - 1; i >= 0; i--) {
- consumer.accept(mActivities.get(i));
- }
- }
- }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 4ebbf49..a94b586 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -163,7 +163,7 @@
@Test
public void testAddChildSetsSurfacePosition() {
reset(mTransaction);
- try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
+ try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mDisplayContent)) {
WindowContainer child = new WindowContainer(mWm);
child.setBounds(1, 1, 10, 10);
@@ -266,7 +266,7 @@
@Test
public void testRemoveImmediatelyClearsLastSurfacePosition() {
reset(mTransaction);
- try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
+ try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mDisplayContent)) {
final WindowContainer<WindowContainer> child1 = new WindowContainer(mWm);
child1.setBounds(1, 1, 10, 10);
@@ -1827,8 +1827,9 @@
implements AutoCloseable {
private final SurfaceSession mSession = new SurfaceSession();
- MockSurfaceBuildingContainer(WindowManagerService wm) {
- super(wm);
+ MockSurfaceBuildingContainer(DisplayContent dc) {
+ super(dc.mWmService);
+ onDisplayChanged(dc);
}
static class MockSurfaceBuilder extends SurfaceControl.Builder {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 24ebad6..fcf7a3f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -78,7 +78,6 @@
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.InputConfig;
import android.os.Process;
@@ -94,7 +93,6 @@
import android.util.MergedConfiguration;
import android.view.ContentRecordingSession;
import android.view.IWindow;
-import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -265,69 +263,7 @@
}
@Test
- public void testRelayoutExitingWindow_legacy() {
- mSetFlagsRule.disableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
-
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
- final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
- win.mWinAnimator.mSurfaceController = surfaceController;
- win.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
- doReturn(true).when(surfaceController).hasSurface();
- spyOn(win.mTransitionController);
- doReturn(true).when(win.mTransitionController).isShellTransitionsEnabled();
- doReturn(true).when(win.mTransitionController).inTransition(
- eq(win.mActivityRecord));
- win.mViewVisibility = View.VISIBLE;
- win.mHasSurface = true;
- win.mActivityRecord.mAppStopped = true;
- mWm.mWindowMap.put(win.mClient.asBinder(), win);
- spyOn(mWm.mWindowPlacerLocked);
- // Skip unnecessary operations of relayout.
- doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
- final int w = 100;
- final int h = 200;
- final ClientWindowFrames outFrames = new ClientWindowFrames();
- final MergedConfiguration outConfig = new MergedConfiguration();
- final SurfaceControl outSurfaceControl = new SurfaceControl();
- final InsetsState outInsetsState = new InsetsState();
- final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
- final Bundle outBundle = new Bundle();
- mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
- // The window is in transition, so its destruction is deferred.
- assertTrue(win.mAnimatingExit);
- assertFalse(win.mDestroying);
- assertTrue(win.mTransitionController.mAnimatingExitWindows.contains(win));
-
- win.mAnimatingExit = false;
- win.mViewVisibility = View.VISIBLE;
- win.mActivityRecord.setVisibleRequested(false);
- win.mActivityRecord.setVisible(false);
- mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
- // Because the window is already invisible, it doesn't need to apply exiting animation
- // and WMS#tryStartExitingAnimation() will destroy the surface directly.
- assertFalse(win.mAnimatingExit);
- assertFalse(win.mHasSurface);
- assertNull(win.mWinAnimator.mSurfaceController);
-
- // Invisible requested activity should not get the last config even if its view is visible.
- mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
- assertEquals(0, outConfig.getMergedConfiguration().densityDpi);
- // Non activity window can still get the last config.
- win.mActivityRecord = null;
- win.fillClientWindowFramesAndConfiguration(outFrames, outConfig,
- null /* outActivityWindowInfo*/, false /* useLatestConfig */,
- true /* relayoutVisible */);
- assertEquals(win.getConfiguration().densityDpi,
- outConfig.getMergedConfiguration().densityDpi);
- }
-
- @Test
public void testRelayoutExitingWindow() {
- mSetFlagsRule.enableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
-
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
win.mWinAnimator.mSurfaceController = surfaceController;
@@ -483,15 +419,8 @@
win.mRelayoutSeq = 1;
seq = 2;
}
- if (Flags.windowSessionRelayoutInfo()) {
- mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
- 0, new WindowRelayoutResult());
- } else {
- mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
- 0, new ClientWindowFrames(), new MergedConfiguration(),
- new SurfaceControl(), new InsetsState(), new InsetsSourceControl.Array(),
- new Bundle());
- }
+ mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
+ 0, new WindowRelayoutResult());
ArgumentCaptor<Integer> changedFlags = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> changedPrivateFlags = ArgumentCaptor.forClass(Integer.class);
@@ -1364,70 +1293,8 @@
}
@Test
- public void testRelayout_appWindowSendActivityWindowInfo_legacy() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
- mSetFlagsRule.disableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
-
- // Skip unnecessary operations of relayout.
- spyOn(mWm.mWindowPlacerLocked);
- doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
-
- final Task task = createTask(mDisplayContent);
- final WindowState win = createAppWindow(task, ACTIVITY_TYPE_STANDARD, "appWindow");
- mWm.mWindowMap.put(win.mClient.asBinder(), win);
-
- final int w = 100;
- final int h = 200;
- final ClientWindowFrames outFrames = new ClientWindowFrames();
- final MergedConfiguration outConfig = new MergedConfiguration();
- final SurfaceControl outSurfaceControl = new SurfaceControl();
- final InsetsState outInsetsState = new InsetsState();
- final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
- final Bundle outBundle = new Bundle();
-
- final ActivityRecord activity = win.mActivityRecord;
- final ActivityWindowInfo expectedInfo = new ActivityWindowInfo();
- expectedInfo.set(true, new Rect(0, 0, 1000, 2000), new Rect(0, 0, 500, 2000));
- doReturn(expectedInfo).when(activity).getActivityWindowInfo();
- activity.setVisibleRequested(false);
- activity.setVisible(false);
-
- mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
-
- // No latest reported value, so return empty when activity is invisible
- final ActivityWindowInfo activityWindowInfo = outBundle.getParcelable(
- IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO, ActivityWindowInfo.class);
- assertEquals(new ActivityWindowInfo(), activityWindowInfo);
-
- activity.setVisibleRequested(true);
- activity.setVisible(true);
-
- mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
-
- // Report the latest when activity is visible.
- final ActivityWindowInfo activityWindowInfo2 = outBundle.getParcelable(
- IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO, ActivityWindowInfo.class);
- assertEquals(expectedInfo, activityWindowInfo2);
-
- expectedInfo.set(false, new Rect(0, 0, 1000, 2000), new Rect(0, 0, 1000, 2000));
- activity.setVisibleRequested(false);
- activity.setVisible(false);
-
- mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
-
- // Report the last reported value when activity is invisible.
- final ActivityWindowInfo activityWindowInfo3 = outBundle.getParcelable(
- IWindowSession.KEY_RELAYOUT_BUNDLE_ACTIVITY_WINDOW_INFO, ActivityWindowInfo.class);
- assertEquals(activityWindowInfo2, activityWindowInfo3);
- }
-
- @Test
public void testRelayout_appWindowSendActivityWindowInfo() {
mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
- mSetFlagsRule.enableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
// Skip unnecessary operations of relayout.
spyOn(mWm.mWindowPlacerLocked);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 059fed20..e6648da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -340,54 +340,33 @@
public void testComputeOomAdjFromActivities() {
final ActivityRecord activity = createActivityRecord(mWpc);
activity.setVisibleRequested(true);
- final int[] callbackResult = { 0 };
- final int visible = 1;
- final int paused = 2;
- final int stopping = 4;
- final int other = 8;
- final WindowProcessController.ComputeOomAdjCallback callback =
- new WindowProcessController.ComputeOomAdjCallback() {
- @Override
- public void onVisibleActivity() {
- callbackResult[0] |= visible;
- }
-
- @Override
- public void onPausedActivity() {
- callbackResult[0] |= paused;
- }
-
- @Override
- public void onStoppingActivity(boolean finishing) {
- callbackResult[0] |= stopping;
- }
-
- @Override
- public void onOtherActivity() {
- callbackResult[0] |= other;
- }
- };
// onStartActivity should refresh the state immediately.
mWpc.onStartActivity(0 /* topProcessState */, activity.info);
- assertEquals(1 /* minTaskLayer */, mWpc.computeOomAdjFromActivities(callback));
- assertEquals(visible, callbackResult[0]);
+ int flags = mWpc.getActivityStateFlags();
+ assertEquals(1 /* minTaskLayer */,
+ flags & WindowProcessController.ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER);
+ final int visibleFlags = WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
+ assertEquals(visibleFlags,
+ flags & ~WindowProcessController.ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER);
- callbackResult[0] = 0;
activity.setVisibleRequested(false);
activity.setState(PAUSED, "test");
- mWpc.computeOomAdjFromActivities(callback);
- assertEquals(paused, callbackResult[0]);
+ final int exclusiveFlags = WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ | WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED
+ | WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
+ flags = mWpc.getActivityStateFlags() & exclusiveFlags;
+ assertEquals(WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED, flags);
- callbackResult[0] = 0;
activity.setState(STOPPING, "test");
- mWpc.computeOomAdjFromActivities(callback);
- assertEquals(stopping, callbackResult[0]);
+ flags = mWpc.getActivityStateFlags() & exclusiveFlags;
+ assertEquals(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING, flags);
- callbackResult[0] = 0;
activity.setState(STOPPED, "test");
- mWpc.computeOomAdjFromActivities(callback);
- assertEquals(other, callbackResult[0]);
+ flags = mWpc.getActivityStateFlags() & exclusiveFlags;
+ assertEquals(0, flags);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index b152c3e..e13376b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -86,7 +86,6 @@
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.InputConfig;
import android.os.RemoteException;
@@ -114,7 +113,6 @@
import com.android.server.testutils.StubTransaction;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
-import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Test;
@@ -1369,67 +1367,7 @@
@SetupWindows(addWindows = {W_INPUT_METHOD})
@Test
- public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged_legacy() {
- mSetFlagsRule.disableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
-
- final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
- mWm.mImeTargetChangeListener = listener;
-
- // Scenario 1: test addWindow/relayoutWindow to add Ime layering overlay window as visible.
- final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
- mDisplayContent);
- final IWindow client = new TestIWindow();
- final Session session = getTestSession();
- final ClientWindowFrames outFrames = new ClientWindowFrames();
- final MergedConfiguration outConfig = new MergedConfiguration();
- final SurfaceControl outSurfaceControl = new SurfaceControl();
- final InsetsState outInsetsState = new InsetsState();
- final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
- final Bundle outBundle = new Bundle();
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- TYPE_APPLICATION_OVERLAY);
- params.setTitle("imeLayeringTargetOverlay");
- params.token = windowToken.token;
- params.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
-
- mWm.addWindow(session, client, params, View.VISIBLE, DEFAULT_DISPLAY,
- 0 /* userUd */, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
- new InsetsSourceControl.Array(), new Rect(), new float[1]);
- mWm.relayoutWindow(session, client, params, 100, 200, View.VISIBLE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
- waitHandlerIdle(mWm.mH);
-
- final WindowState imeLayeringTargetOverlay = mDisplayContent.getWindow(
- w -> w.mClient.asBinder() == client.asBinder());
- assertThat(imeLayeringTargetOverlay.isVisible()).isTrue();
- assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
- assertThat(listener.mIsRemoved).isFalse();
- assertThat(listener.mIsVisibleForImeTargetOverlay).isTrue();
-
- // Scenario 2: test relayoutWindow to let the Ime layering target overlay window invisible.
- mWm.relayoutWindow(session, client, params, 100, 200, View.GONE, 0, 0, 0,
- outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
- waitHandlerIdle(mWm.mH);
-
- assertThat(imeLayeringTargetOverlay.isVisible()).isFalse();
- assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
- assertThat(listener.mIsRemoved).isFalse();
- assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
-
- // Scenario 3: test removeWindow to remove the Ime layering target overlay window.
- mWm.removeClientToken(session, client.asBinder());
- waitHandlerIdle(mWm.mH);
-
- assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
- assertThat(listener.mIsRemoved).isTrue();
- assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
- }
-
- @SetupWindows(addWindows = {W_INPUT_METHOD})
- @Test
public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() {
- mSetFlagsRule.enableFlags(Flags.FLAG_WINDOW_SESSION_RELAYOUT_INFO);
-
final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
mWm.mImeTargetChangeListener = listener;
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CurrentTimeMillisSupplierFake.java b/services/tests/wmtests/src/com/android/server/wm/utils/CurrentTimeMillisSupplierFake.java
new file mode 100644
index 0000000..f4b2adb
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CurrentTimeMillisSupplierFake.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import java.util.function.LongSupplier;
+
+/**
+ * Fake {@link LongSupplier} implementation used in tests.
+ */
+public class CurrentTimeMillisSupplierFake implements LongSupplier {
+
+ private long mCurrenTimeMillis = System.currentTimeMillis();
+
+ /**
+ * @return The currentTimeMilles used in test
+ */
+ @Override
+ public long getAsLong() {
+ return mCurrenTimeMillis;
+ }
+
+ /**
+ * Simulate some time passed.
+ * @param delayInMillis The delay in milliseconds,
+ */
+ public void delay(long delayInMillis) {
+ mCurrenTimeMillis += delayInMillis;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/TestComponentStack.java b/services/tests/wmtests/src/com/android/server/wm/utils/TestComponentStack.java
new file mode 100644
index 0000000..a06c0a2
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/TestComponentStack.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Contains all the component created in the test.
+ */
+public class TestComponentStack<T> {
+
+ @NonNull
+ private final List<T> mItems = new ArrayList<>();
+
+ /**
+ * Adds an item to the stack.
+ *
+ * @param item The item to add.
+ */
+ public void push(@NonNull T item) {
+ mItems.add(item);
+ }
+
+ /**
+ * Consumes the top element of the stack.
+ *
+ * @param consumer Consumer for the optional top element.
+ * @throws IndexOutOfBoundsException In case that stack is empty.
+ */
+ public void applyToTop(@NonNull Consumer<T> consumer) {
+ consumer.accept(top());
+ }
+
+ /**
+ * Returns the item at fromTop position from the top one if present or it throws an
+ * exception if not present.
+ *
+ * @param fromTop The position from the top of the item to return.
+ * @return The returned item.
+ * @throws IndexOutOfBoundsException In case that position doesn't exist.
+ */
+ @NonNull
+ public T getFromTop(int fromTop) {
+ return mItems.get(mItems.size() - fromTop - 1);
+ }
+
+ /**
+ * @return The item at the base of the stack if present.
+ * @throws IndexOutOfBoundsException In case that stack is empty.
+ */
+ @NonNull
+ public T base() {
+ return mItems.get(0);
+ }
+
+ /**
+ * @return The item at the top of the stack if present.
+ * @throws IndexOutOfBoundsException In case that stack is empty.
+ */
+ @NonNull
+ public T top() {
+ return mItems.get(mItems.size() - 1);
+ }
+
+ /**
+ * @return {@code true} if the stack is empty.
+ */
+ public boolean isEmpty() {
+ return mItems.isEmpty();
+ }
+
+ /**
+ * Allows access to the item at position beforeLast from the top.
+ *
+ * @param fromTop The position from the top of the item to return.
+ * @param consumer Consumer for the optional returned element.
+ */
+ public void applyTo(int fromTop, @NonNull Consumer<T> consumer) {
+ consumer.accept(getFromTop(fromTop));
+ }
+
+ /**
+ * Invoked the consumer iterating over all the elements in the stack.
+ *
+ * @param consumer Consumer for the elements.
+ */
+ public void applyToAll(@NonNull Consumer<T> consumer) {
+ for (int i = mItems.size() - 1; i >= 0; i--) {
+ consumer.accept(mItems.get(i));
+ }
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index a8a9017..ba33eab 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -33,6 +33,7 @@
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -136,6 +137,7 @@
// The obfuscated packages to tokens mappings file
private final File mPackageMappingsFile;
// Holds all of the data related to the obfuscated packages and their token mappings.
+ @GuardedBy("mLock")
final PackagesTokenData mPackagesTokenData = new PackagesTokenData();
/**
@@ -771,27 +773,30 @@
* all of the stats at once has an amortized cost for future calls.
*/
void filterStats(IntervalStats stats) {
- if (mPackagesTokenData.removedPackagesMap.isEmpty()) {
- return;
- }
- final ArrayMap<String, Long> removedPackagesMap = mPackagesTokenData.removedPackagesMap;
-
- // filter out package usage stats
- final int removedPackagesSize = removedPackagesMap.size();
- for (int i = 0; i < removedPackagesSize; i++) {
- final String removedPackage = removedPackagesMap.keyAt(i);
- final UsageStats usageStats = stats.packageStats.get(removedPackage);
- if (usageStats != null && usageStats.mEndTimeStamp < removedPackagesMap.valueAt(i)) {
- stats.packageStats.remove(removedPackage);
+ synchronized (mLock) {
+ if (mPackagesTokenData.removedPackagesMap.isEmpty()) {
+ return;
}
- }
+ final ArrayMap<String, Long> removedPackagesMap = mPackagesTokenData.removedPackagesMap;
- // filter out events
- for (int i = stats.events.size() - 1; i >= 0; i--) {
- final UsageEvents.Event event = stats.events.get(i);
- final Long timeRemoved = removedPackagesMap.get(event.mPackage);
- if (timeRemoved != null && timeRemoved > event.mTimeStamp) {
- stats.events.remove(i);
+ // filter out package usage stats
+ final int removedPackagesSize = removedPackagesMap.size();
+ for (int i = 0; i < removedPackagesSize; i++) {
+ final String removedPackage = removedPackagesMap.keyAt(i);
+ final UsageStats usageStats = stats.packageStats.get(removedPackage);
+ if (usageStats != null &&
+ usageStats.mEndTimeStamp < removedPackagesMap.valueAt(i)) {
+ stats.packageStats.remove(removedPackage);
+ }
+ }
+
+ // filter out events
+ for (int i = stats.events.size() - 1; i >= 0; i--) {
+ final UsageEvents.Event event = stats.events.get(i);
+ final Long timeRemoved = removedPackagesMap.get(event.mPackage);
+ if (timeRemoved != null && timeRemoved > event.mTimeStamp) {
+ stats.events.remove(i);
+ }
}
}
}
@@ -1226,12 +1231,14 @@
}
void obfuscateCurrentStats(IntervalStats[] currentStats) {
- if (mCurrentVersion < 5) {
- return;
- }
- for (int i = 0; i < currentStats.length; i++) {
- final IntervalStats stats = currentStats[i];
- stats.obfuscateData(mPackagesTokenData);
+ synchronized (mLock) {
+ if (mCurrentVersion < 5) {
+ return;
+ }
+ for (int i = 0; i < currentStats.length; i++) {
+ final IntervalStats stats = currentStats[i];
+ stats.obfuscateData(mPackagesTokenData);
+ }
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 1d01420..7e7a531 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1055,15 +1055,22 @@
if (sessionArgs != null && sessionArgs.containsKey(csKey)) {
if (sessionArgs.getBoolean(csEnabledKey, true)) {
// If Contextual Search is enabled, try to follow that path.
- Intent launchIntent = getContextualSearchIntent(sessionArgs);
+ Intent launchIntent;
+ final long getSearchIntentCaller = Binder.clearCallingIdentity();
+ try {
+ launchIntent = getContextualSearchIntent(sessionArgs);
+ } finally {
+ Binder.restoreCallingIdentity(getSearchIntentCaller);
+ }
if (launchIntent != null) {
// Hand over to contextual search helper.
Slog.d(TAG, "Handed over to contextual search helper.");
- final long caller = Binder.clearCallingIdentity();
+ final int userId = Binder.getCallingUserHandle().getIdentifier();
+ final long startSearchCaller = Binder.clearCallingIdentity();
try {
- return startContextualSearch(launchIntent);
+ return startContextualSearch(launchIntent, userId);
} finally {
- Binder.restoreCallingIdentity(caller);
+ Binder.restoreCallingIdentity(startSearchCaller);
}
}
}
@@ -2761,7 +2768,7 @@
}
@RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS)
- private boolean startContextualSearch(Intent launchIntent) {
+ private boolean startContextualSearch(Intent launchIntent, final int userId) {
// Contextual search starts with a frozen screen - so we launch without
// any system animations or starting window.
final ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(mContext,
@@ -2769,7 +2776,7 @@
opts.setDisableStartingWindow(true);
int resultCode = mAtmInternal.startActivityWithScreenshot(launchIntent,
mContext.getPackageName(), Binder.getCallingUid(), Binder.getCallingPid(), null,
- opts.toBundle(), Binder.getCallingUserHandle().getIdentifier());
+ opts.toBundle(), userId);
return resultCode == ActivityManager.START_SUCCESS;
}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 0ecafc7..019fb7b 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -49,6 +49,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -1285,6 +1286,13 @@
private static final String SINGAPORE_ISO_COUNTRY_CODE = "SG";
+ private static final String[] COUNTRY_CODES_TO_FORMAT_NATIONALLY = new String[] {
+ "KR", // Korea
+ "JP", // Japan
+ "SG", // Singapore
+ "TW", // Taiwan
+ };
+
/**
* Breaks the given number down and formats it according to the rules
* for the country the number is from.
@@ -1647,45 +1655,58 @@
defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
}
+ Rlog.v(LOG_TAG, "formatNumber: defaultCountryIso: " + defaultCountryIso);
+
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
String result = null;
try {
PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
- if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
- (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
- (pn.getCountryCodeSource() ==
- PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
- /**
- * Need to reformat any local Korean phone numbers (when the user is in Korea) with
- * country code to corresponding national format which would replace the leading
- * +82 with 0.
- */
- result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
- } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
- pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) &&
- (pn.getCountryCodeSource() ==
- PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
- /**
- * Need to reformat Japanese phone numbers (when user is in Japan) with the national
- * dialing format.
- */
- result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
- } else if (Flags.removeCountryCodeFromLocalSingaporeCalls() &&
- (SINGAPORE_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
- pn.getCountryCode() ==
- util.getCountryCodeForRegion(SINGAPORE_ISO_COUNTRY_CODE) &&
- (pn.getCountryCodeSource() ==
- PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN))) {
- /*
- * Need to reformat Singaporean phone numbers (when the user is in Singapore)
- * with the country code (+65) removed to comply with Singaporean regulations.
- */
- result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ if (Flags.nationalCountryCodeFormattingForLocalCalls()) {
+ if (Arrays.asList(COUNTRY_CODES_TO_FORMAT_NATIONALLY).contains(defaultCountryIso)
+ && pn.getCountryCode() == util.getCountryCodeForRegion(defaultCountryIso)
+ && pn.getCountryCodeSource()
+ == PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN) {
+ return util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else {
+ return util.formatInOriginalFormat(pn, defaultCountryIso);
+ }
} else {
- result = util.formatInOriginalFormat(pn, defaultCountryIso);
+ if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) && (
+ pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE))
+ && (pn.getCountryCodeSource()
+ == PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+ /**
+ * Need to reformat any local Korean phone numbers (when the user is in
+ * Korea) with country code to corresponding national format which would
+ * replace the leading +82 with 0.
+ */
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso)
+ && pn.getCountryCode() == util.getCountryCodeForRegion(
+ JAPAN_ISO_COUNTRY_CODE) && (pn.getCountryCodeSource()
+ == PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+ /**
+ * Need to reformat Japanese phone numbers (when user is in Japan) with the
+ * national dialing format.
+ */
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else if (Flags.removeCountryCodeFromLocalSingaporeCalls() && (
+ SINGAPORE_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso)
+ && pn.getCountryCode() == util.getCountryCodeForRegion(
+ SINGAPORE_ISO_COUNTRY_CODE) && (pn.getCountryCodeSource()
+ == PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN))) {
+ /*
+ * Need to reformat Singaporean phone numbers (when the user is in Singapore)
+ * with the country code (+65) removed to comply with Singaporean regulations.
+ */
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else {
+ result = util.formatInOriginalFormat(pn, defaultCountryIso);
+ }
}
} catch (NumberParseException e) {
+ if (DBG) log("formatNumber: NumberParseException caught " + e);
}
return result;
}
diff --git a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
index eadb726..2b515c9 100644
--- a/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
+++ b/telephony/java/android/telephony/VisualVoicemailSmsFilterSettings.java
@@ -64,6 +64,14 @@
* @hide
*/
public static final int DEFAULT_DESTINATION_PORT = DESTINATION_PORT_ANY;
+ /**
+ * @hide
+ */
+ public static final int MAX_STRING_LENGTH = 256;
+ /**
+ * @hide
+ */
+ public static final int MAX_LIST_SIZE = 100;
/**
* Builder class for {@link VisualVoicemailSmsFilterSettings} objects.
@@ -82,11 +90,16 @@
/**
* Sets the client prefix for the visual voicemail SMS filter. The client prefix will appear
* at the start of a visual voicemail SMS message, followed by a colon(:).
+ * @throws IllegalArgumentException if the string length is greater than 256 characters
*/
public Builder setClientPrefix(String clientPrefix) {
if (clientPrefix == null) {
throw new IllegalArgumentException("Client prefix cannot be null");
}
+ if (clientPrefix.length() > MAX_STRING_LENGTH) {
+ throw new IllegalArgumentException("Client prefix cannot be greater than "
+ + MAX_STRING_LENGTH + " characters");
+ }
mClientPrefix = clientPrefix;
return this;
}
@@ -95,11 +108,25 @@
* Sets the originating number allow list for the visual voicemail SMS filter. If the list
* is not null only the SMS messages from a number in the list can be considered as a visual
* voicemail SMS. Otherwise, messages from any address will be considered.
+ * @throws IllegalArgumentException if the size of the originatingNumbers list is greater
+ * than 100 elements
+ * @throws IllegalArgumentException if an element within the originatingNumbers list has
+ * a string length greater than 256
*/
public Builder setOriginatingNumbers(List<String> originatingNumbers) {
if (originatingNumbers == null) {
throw new IllegalArgumentException("Originating numbers cannot be null");
}
+ if (originatingNumbers.size() > MAX_LIST_SIZE) {
+ throw new IllegalArgumentException("The originatingNumbers list size cannot be"
+ + " greater than " + MAX_STRING_LENGTH + " elements");
+ }
+ for (String num : originatingNumbers) {
+ if (num != null && num.length() > MAX_STRING_LENGTH) {
+ throw new IllegalArgumentException("Numbers within the originatingNumbers list"
+ + " cannot be greater than" + MAX_STRING_LENGTH + " characters");
+ }
+ }
mOriginatingNumbers = originatingNumbers;
return this;
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index e29d321..5976657 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -51,10 +51,8 @@
java_library {
name: "android.test.mock.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-java"],
srcs: [":android-test-mock-sources"],
- visibility: [
- "//frameworks/base",
- ],
}
android_ravenwood_test {
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
index 57303e7..64775f8 100644
--- a/tests/BootImageProfileTest/OWNERS
+++ b/tests/BootImageProfileTest/OWNERS
@@ -1,4 +1 @@
-calin@google.com
-ngeoffray@google.com
-vmarko@google.com
-yawanng@google.com
+include platform/art:main:/OWNERS_boot_profile
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index 63782f1..1842f0a 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+// InputMonitor is deprecated, but we still need to test it.
+@file:Suppress("DEPRECATION")
+
package com.android.test.input
import android.app.Activity
@@ -43,6 +46,7 @@
}
private lateinit var mInputEventReceiver: InputEventReceiver
private lateinit var mInputMonitor: InputMonitor
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val inputManager = checkNotNull(getSystemService(InputManager::class.java))
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index d0e5626..0c3c7e2 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -17,9 +17,6 @@
package android.trust.test
import android.content.pm.PackageManager
-import android.platform.test.annotations.RequiresFlagsDisabled
-import android.platform.test.annotations.RequiresFlagsEnabled
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.service.trust.GrantTrustResult
import android.trust.BaseTrustAgentService
import android.trust.TrustTestActivity
@@ -58,7 +55,6 @@
.around(ScreenLockRule())
.around(lockStateTrackingRule)
.around(trustAgentRule)
- .around(DeviceFlagsValueProvider.createCheckFlagsRule())
@Before
fun manageTrust() {
@@ -93,7 +89,6 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
fun grantCannotActivelyUnlockDevice() {
// On automotive, trust agents can actively unlock the device.
assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
@@ -120,24 +115,6 @@
}
@Test
- @RequiresFlagsDisabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
- fun grantCouldCauseWrongDeviceLockedStateDueToBug() {
- // On automotive, trust agents can actively unlock the device.
- assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
-
- // Verify that b/296464083 exists. That is, when the device is locked
- // and a trust agent grants trust, the deviceLocked state incorrectly
- // becomes false even though the device correctly remains locked.
- uiDevice.sleep()
- lockStateTrackingRule.assertLocked()
- trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0) {}
- uiDevice.wakeUp()
- uiDevice.sleep()
- await()
- lockStateTrackingRule.assertUnlockedButNotReally()
- }
-
- @Test
fun grantDoesNotCallBack() {
val callback = mock<(GrantTrustResult) -> Unit>()
trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 0121809..80d7947 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -64,13 +64,6 @@
wait("not trusted") { trustState.trusted == false }
}
- // TODO(b/299298338) remove this when removing FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2
- fun assertUnlockedButNotReally() {
- wait("device unlocked") { !keyguardManager.isDeviceLocked }
- wait("not trusted") { trustState.trusted == false }
- wait("keyguard locked") { windowManager.isKeyguardLocked }
- }
-
fun assertUnlockedAndTrusted() {
wait("device unlocked") { !keyguardManager.isDeviceLocked }
wait("trusted") { trustState.trusted == true }
diff --git a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java b/tests/testables/src/android/animation/AnimatorTestRule.java
similarity index 100%
rename from packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
rename to tests/testables/src/android/animation/AnimatorTestRule.java
diff --git a/tests/testables/tests/Android.bp b/tests/testables/tests/Android.bp
index 06449e0..d6a4754 100644
--- a/tests/testables/tests/Android.bp
+++ b/tests/testables/tests/Android.bp
@@ -26,14 +26,18 @@
platform_apis: true,
srcs: [
"src/**/*.java",
+ "src/**/*.kt",
"src/**/I*.aidl",
],
resource_dirs: ["res"],
static_libs: [
+ "androidx.core_core-animation",
+ "androidx.core_core-ktx",
"androidx.test.rules",
"hamcrest-library",
"mockito-target-inline-minus-junit4",
"testables",
+ "truth",
],
compile_multilib: "both",
jni_libs: [
diff --git a/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt b/tests/testables/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
similarity index 94%
rename from packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
rename to tests/testables/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
index f23fbee..5abebee 100644
--- a/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
+++ b/tests/testables/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
@@ -19,7 +19,6 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.core.animation.doOnEnd
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -32,7 +31,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper
-class AnimatorTestRuleIsolationTest : SysuiTestCase() {
+class AnimatorTestRuleIsolationTest {
@get:Rule val animatorTestRule = AnimatorTestRule(this)
@@ -41,7 +40,7 @@
// GIVEN global state is reset at the start of the test
didTouchA = false
didTouchB = false
- // WHEN starting 2 animations of different durations, and setting didTouchA at the end
+ // WHEN starting 2 animations of different durations, and setting didTouch{A,B} at the end
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchA = true }
@@ -49,7 +48,7 @@
}
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 150
- doOnEnd { didTouchA = true }
+ doOnEnd { didTouchB = true }
start()
}
// WHEN when you advance time so that only one of the animations has ended
@@ -65,7 +64,7 @@
// GIVEN global state is reset at the start of the test
didTouchA = false
didTouchB = false
- // WHEN starting 2 animations of different durations, and setting didTouchB at the end
+ // WHEN starting 2 animations of different durations, and setting didTouch{A,B} at the end
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 100
doOnEnd { didTouchB = true }
@@ -73,7 +72,7 @@
}
ObjectAnimator.ofFloat(0f, 1f).apply {
duration = 150
- doOnEnd { didTouchB = true }
+ doOnEnd { didTouchA = true }
start()
}
animatorTestRule.advanceTimeBy(100)
diff --git a/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt b/tests/testables/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
rename to tests/testables/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
index fd5f157..9eeaad5 100644
--- a/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
+++ b/tests/testables/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
@@ -17,10 +17,9 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.view.animation.LinearInterpolator
import androidx.core.animation.doOnEnd
import androidx.test.filters.SmallTest
-import com.android.app.animation.Interpolators
-import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -29,7 +28,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper
-class AnimatorTestRulePrecisionTest : SysuiTestCase() {
+class AnimatorTestRulePrecisionTest {
@get:Rule val animatorTestRule = AnimatorTestRule(this)
@@ -43,7 +42,7 @@
crossinline onEndAction: (animator: Animator) -> Unit,
) {
ObjectAnimator.ofFloat(this, propertyName, 0f, 1f).also {
- it.interpolator = Interpolators.LINEAR
+ it.interpolator = LINEAR_INTERPOLATOR
it.duration = duration
it.startDelay = startDelay
it.doOnEnd(onEndAction)
@@ -190,4 +189,8 @@
assertThat(ended2).isTrue()
assertThat(AnimationHandler.getAnimationCount()).isEqualTo(0)
}
+
+ private companion object {
+ private val LINEAR_INTERPOLATOR = LinearInterpolator()
+ }
}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 031dd5b..9b8c3b3 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -836,6 +836,28 @@
return 1;
}
+ // Parse the feature flag values. An argument that starts with '@' points to a file to read flag
+ // values from.
+ std::vector<std::string> all_feature_flags_args;
+ for (const std::string& arg : feature_flags_args_) {
+ if (util::StartsWith(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) {
+ context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
+ return 1;
+ }
+ } else {
+ all_feature_flags_args.push_back(arg);
+ }
+ }
+
+ for (const std::string& arg : all_feature_flags_args) {
+ if (!ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
+ return 1;
+ }
+ }
+
return Compile(&context, file_collection.get(), archive_writer.get(), options_);
}
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 61c5b60..70c8791 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -24,6 +24,7 @@
#include "Command.h"
#include "ResourceTable.h"
#include "androidfw/IDiagnostics.h"
+#include "cmd/Util.h"
#include "format/Archive.h"
#include "process/IResourceTableConsumer.h"
@@ -45,6 +46,7 @@
bool preserve_visibility_of_styleables = false;
bool verbose = false;
std::optional<std::string> product_;
+ FeatureFlagValues feature_flag_values;
};
/** Parses flags and compiles resources to be used in linking. */
@@ -92,6 +94,12 @@
"Leave only resources specific to the given product. All "
"other resources (including defaults) are removed.",
&options_.product_);
+ AddOptionalFlagList("--feature-flags",
+ "Specify the values of feature flags. The pairs in the argument\n"
+ "are separated by ',' the name is separated from the value by '='.\n"
+ "The name can have a suffix of ':ro' to indicate it is read only."
+ "Example: \"flag1=true,flag2:ro=false,flag3=\" (flag3 has no given value).",
+ &feature_flags_args_);
}
int Action(const std::vector<std::string>& args) override;
@@ -101,6 +109,7 @@
CompileOptions options_;
std::optional<std::string> visibility_;
std::optional<std::string> trace_folder_;
+ std::vector<std::string> feature_flags_args_;
};
int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 8fe414f..2f17853 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -332,8 +332,9 @@
AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
AddOptionalFlagList("--feature-flags",
"Specify the values of feature flags. The pairs in the argument\n"
- "are separated by ',' and the name is separated from the value by '='.\n"
- "Example: \"flag1=true,flag2=false,flag3=\" (flag3 has no given value).",
+ "are separated by ',' the name is separated from the value by '='.\n"
+ "The name can have a suffix of ':ro' to indicate it is read only."
+ "Example: \"flag1=true,flag2:ro=false,flag3=\" (flag3 has no given value).",
&feature_flags_args_);
AddOptionalSwitch("--non-updatable-system",
"Mark the app as a non-updatable system app. This inserts\n"
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 678d846..e839fc1 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -128,7 +128,7 @@
if (parts.size() > 2) {
diag->Error(android::DiagMessage()
<< "Invalid feature flag and optional value '" << flag_and_value
- << "'. Must be in the format 'flag_name[=true|false]");
+ << "'. Must be in the format 'flag_name[:ro][=true|false]");
return false;
}
@@ -137,6 +137,25 @@
diag->Error(android::DiagMessage() << "No name given for one or more flags in: " << arg);
return false;
}
+ std::vector<std::string> name_parts = util::Split(flag_name, ':');
+ if (name_parts.size() > 2) {
+ diag->Error(android::DiagMessage()
+ << "Invalid feature flag and optional value '" << flag_and_value
+ << "'. Must be in the format 'flag_name[:ro][=true|false]");
+ return false;
+ }
+ flag_name = name_parts[0];
+ bool read_only = false;
+ if (name_parts.size() == 2) {
+ if (name_parts[1] == "ro") {
+ read_only = true;
+ } else {
+ diag->Error(android::DiagMessage()
+ << "Invalid feature flag and optional value '" << flag_and_value
+ << "'. Must be in the format 'flag_name[:ro][=true|false]");
+ return false;
+ }
+ }
std::optional<bool> flag_value = {};
if (parts.size() == 2) {
@@ -151,13 +170,13 @@
}
}
- if (auto [it, inserted] =
- out_feature_flag_values->try_emplace(std::string(flag_name), flag_value);
+ auto ffp = FeatureFlagProperties{read_only, flag_value};
+ if (auto [it, inserted] = out_feature_flag_values->try_emplace(std::string(flag_name), ffp);
!inserted) {
// We are allowing the same flag to appear multiple times, last value wins.
diag->Note(android::DiagMessage()
<< "Value for feature flag '" << flag_name << "' was given more than once");
- it->second = flag_value;
+ it->second = ffp;
}
}
return true;
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 9ece5dd..6b8813b 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -37,7 +37,17 @@
namespace aapt {
-using FeatureFlagValues = std::map<std::string, std::optional<bool>, std::less<>>;
+struct FeatureFlagProperties {
+ bool read_only;
+ std::optional<bool> enabled;
+
+ FeatureFlagProperties(bool ro, std::optional<bool> e) : read_only(ro), enabled(e) {
+ }
+
+ bool operator==(const FeatureFlagProperties&) const = default;
+};
+
+using FeatureFlagValues = std::map<std::string, FeatureFlagProperties, std::less<>>;
// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
// Returns Nothing and logs a human friendly error message if the string was not legal.
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 723d87e..35bc637 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -383,21 +383,25 @@
TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) {
auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
FeatureFlagValues feature_flag_values;
- ASSERT_TRUE(
- ParseFeatureFlagsParameter("foo=true,bar=true,foo=false", diagnostics, &feature_flag_values));
- EXPECT_THAT(feature_flag_values, UnorderedElementsAre(Pair("foo", std::optional<bool>(false)),
- Pair("bar", std::optional<bool>(true))));
+ ASSERT_TRUE(ParseFeatureFlagsParameter("foo=true,bar=true,foo:ro=false", diagnostics,
+ &feature_flag_values));
+ EXPECT_THAT(
+ feature_flag_values,
+ UnorderedElementsAre(Pair("foo", FeatureFlagProperties{true, std::optional<bool>(false)}),
+ Pair("bar", FeatureFlagProperties{false, std::optional<bool>(true)})));
}
TEST(UtilTest, ParseFeatureFlagsParameter_Valid) {
auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
FeatureFlagValues feature_flag_values;
- ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar =FALSE,baz=, quux", diagnostics,
+ ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar:ro =FALSE,baz=, quux", diagnostics,
&feature_flag_values));
- EXPECT_THAT(feature_flag_values,
- UnorderedElementsAre(Pair("foo", std::optional<bool>(true)),
- Pair("bar", std::optional<bool>(false)),
- Pair("baz", std::nullopt), Pair("quux", std::nullopt)));
+ EXPECT_THAT(
+ feature_flag_values,
+ UnorderedElementsAre(Pair("foo", FeatureFlagProperties{false, std::optional<bool>(true)}),
+ Pair("bar", FeatureFlagProperties{true, std::optional<bool>(false)}),
+ Pair("baz", FeatureFlagProperties{false, std::nullopt}),
+ Pair("quux", FeatureFlagProperties{false, std::nullopt})));
}
TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
diff --git a/tools/aapt2/link/FeatureFlagsFilter.cpp b/tools/aapt2/link/FeatureFlagsFilter.cpp
index fdf3f74..9d40db5 100644
--- a/tools/aapt2/link/FeatureFlagsFilter.cpp
+++ b/tools/aapt2/link/FeatureFlagsFilter.cpp
@@ -63,12 +63,11 @@
flag_name = flag_name.substr(1);
}
- if (auto it = feature_flag_values_.find(std::string(flag_name));
- it != feature_flag_values_.end()) {
- if (it->second.has_value()) {
+ if (auto it = feature_flag_values_.find(flag_name); it != feature_flag_values_.end()) {
+ if (it->second.enabled.has_value()) {
if (options_.remove_disabled_elements) {
// Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag"
- return *it->second == negated;
+ return *it->second.enabled == negated;
}
} else if (options_.flags_must_have_value) {
diagnostics_->Error(android::DiagMessage(node->line_number)
diff --git a/tools/aapt2/link/FeatureFlagsFilter_test.cpp b/tools/aapt2/link/FeatureFlagsFilter_test.cpp
index 53086cc..2db2899 100644
--- a/tools/aapt2/link/FeatureFlagsFilter_test.cpp
+++ b/tools/aapt2/link/FeatureFlagsFilter_test.cpp
@@ -48,7 +48,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" />
</manifest>)EOF",
- {{"flag", false}});
+ {{"flag", FeatureFlagProperties{false, false}}});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -60,7 +60,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="flag" />
</manifest>)EOF",
- {{"flag", false}});
+ {{"flag", FeatureFlagProperties{false, false}}});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -73,7 +73,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="!flag" />
</manifest>)EOF",
- {{"flag", true}});
+ {{"flag", FeatureFlagProperties{false, true}}});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -86,7 +86,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="flag" />
</manifest>)EOF",
- {{"flag", true}});
+ {{"flag", FeatureFlagProperties{false, true}}});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -102,7 +102,7 @@
<permission android:name="FOO" android:featureFlag="flag"
android:protectionLevel="dangerous" />
</manifest>)EOF",
- {{"flag", true}});
+ {{"flag", FeatureFlagProperties{false, true}}});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -123,7 +123,7 @@
</activity>
</application>
</manifest>)EOF",
- {{"flag", true}});
+ {{"flag", FeatureFlagProperties{false, true}}});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -145,7 +145,7 @@
</activity>
</application>
</manifest>)EOF",
- {{"flag", true}});
+ {{"flag", FeatureFlagProperties{false, true}}});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -162,7 +162,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag=" " />
</manifest>)EOF",
- {{"flag", false}});
+ {{"flag", FeatureFlagProperties{false, false}}});
ASSERT_THAT(doc, IsNull());
}
@@ -171,7 +171,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="flag" />
</manifest>)EOF",
- {{"flag", std::nullopt}});
+ {{"flag", FeatureFlagProperties{false, std::nullopt}}});
ASSERT_THAT(doc, IsNull());
}
@@ -180,7 +180,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="unrecognized" />
</manifest>)EOF",
- {{"flag", true}});
+ {{"flag", FeatureFlagProperties{false, true}}});
ASSERT_THAT(doc, IsNull());
}
@@ -190,7 +190,7 @@
<permission android:name="FOO" android:featureFlag="bar" />
<permission android:name="FOO" android:featureFlag="unrecognized" />
</manifest>)EOF",
- {{"flag", std::nullopt}});
+ {{"flag", FeatureFlagProperties{false, std::nullopt}}});
ASSERT_THAT(doc, IsNull());
}
@@ -199,7 +199,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="flag" />
</manifest>)EOF",
- {{"flag", false}}, {.remove_disabled_elements = false});
+ {{"flag", FeatureFlagProperties{false, false}}},
+ {.remove_disabled_elements = false});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -212,7 +213,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="flag" />
</manifest>)EOF",
- {{"flag", std::nullopt}}, {.flags_must_have_value = false});
+ {{"flag", FeatureFlagProperties{false, std::nullopt}}},
+ {.flags_must_have_value = false});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
@@ -225,7 +227,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
<permission android:name="FOO" android:featureFlag="unrecognized" />
</manifest>)EOF",
- {{"flag", true}}, {.fail_on_unrecognized_flags = false});
+ {{"flag", FeatureFlagProperties{false, true}}},
+ {.fail_on_unrecognized_flags = false});
ASSERT_THAT(doc, NotNull());
auto root = doc->root.get();
ASSERT_THAT(root, NotNull());
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
index 129733e..f397225 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfo.java
@@ -25,99 +25,42 @@
/** AppInfo representation */
public class AppInfo implements AslMarshallable {
- private final String mTitle;
- private final String mDescription;
- private final Boolean mContainsAds;
- private final Boolean mObeyAps;
- private final Boolean mAdsFingerprinting;
- private final Boolean mSecurityFingerprinting;
+ private final Boolean mApsCompliant;
private final String mPrivacyPolicy;
- private final List<String> mSecurityEndpoints;
private final List<String> mFirstPartyEndpoints;
private final List<String> mServiceProviderEndpoints;
- private final String mCategory;
- private final String mEmail;
- private final String mWebsite;
public AppInfo(
- String title,
- String description,
- Boolean containsAds,
- Boolean obeyAps,
- Boolean adsFingerprinting,
- Boolean securityFingerprinting,
+ Boolean apsCompliant,
String privacyPolicy,
- List<String> securityEndpoints,
List<String> firstPartyEndpoints,
- List<String> serviceProviderEndpoints,
- String category,
- String email,
- String website) {
- this.mTitle = title;
- this.mDescription = description;
- this.mContainsAds = containsAds;
- this.mObeyAps = obeyAps;
- this.mAdsFingerprinting = adsFingerprinting;
- this.mSecurityFingerprinting = securityFingerprinting;
+ List<String> serviceProviderEndpoints) {
+ this.mApsCompliant = apsCompliant;
this.mPrivacyPolicy = privacyPolicy;
- this.mSecurityEndpoints = securityEndpoints;
this.mFirstPartyEndpoints = firstPartyEndpoints;
this.mServiceProviderEndpoints = serviceProviderEndpoints;
- this.mCategory = category;
- this.mEmail = email;
- this.mWebsite = website;
}
/** Creates an on-device DOM element from the {@link SafetyLabels}. */
@Override
public List<Element> toOdDomElements(Document doc) {
Element appInfoEle = XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_APP_INFO);
- if (this.mTitle != null) {
- appInfoEle.appendChild(XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_TITLE, mTitle));
- }
- if (this.mDescription != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_DESCRIPTION, mDescription));
- }
- if (this.mContainsAds != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdBooleanEle(doc, XmlUtils.OD_NAME_CONTAINS_ADS, mContainsAds));
- }
- if (this.mObeyAps != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdBooleanEle(doc, XmlUtils.OD_NAME_OBEY_APS, mObeyAps));
- }
- if (this.mAdsFingerprinting != null) {
+ if (this.mApsCompliant != null) {
appInfoEle.appendChild(
XmlUtils.createOdBooleanEle(
- doc, XmlUtils.OD_NAME_ADS_FINGERPRINTING, mAdsFingerprinting));
- }
- if (this.mSecurityFingerprinting != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdBooleanEle(
- doc,
- XmlUtils.OD_NAME_SECURITY_FINGERPRINTING,
- mSecurityFingerprinting));
+ doc, XmlUtils.OD_NAME_APS_COMPLIANT, mApsCompliant));
}
if (this.mPrivacyPolicy != null) {
appInfoEle.appendChild(
XmlUtils.createOdStringEle(
doc, XmlUtils.OD_NAME_PRIVACY_POLICY, mPrivacyPolicy));
}
- if (this.mSecurityEndpoints != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdArray(
- doc,
- XmlUtils.OD_TAG_STRING_ARRAY,
- XmlUtils.OD_NAME_SECURITY_ENDPOINT,
- mSecurityEndpoints));
- }
if (this.mFirstPartyEndpoints != null) {
appInfoEle.appendChild(
XmlUtils.createOdArray(
doc,
XmlUtils.OD_TAG_STRING_ARRAY,
- XmlUtils.OD_NAME_FIRST_PARTY_ENDPOINT,
+ XmlUtils.OD_NAME_FIRST_PARTY_ENDPOINTS,
mFirstPartyEndpoints));
}
if (this.mServiceProviderEndpoints != null) {
@@ -125,21 +68,9 @@
XmlUtils.createOdArray(
doc,
XmlUtils.OD_TAG_STRING_ARRAY,
- XmlUtils.OD_NAME_SERVICE_PROVIDER_ENDPOINT,
+ XmlUtils.OD_NAME_SERVICE_PROVIDER_ENDPOINTS,
mServiceProviderEndpoints));
}
- if (this.mCategory != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_CATEGORY, this.mCategory));
- }
- if (this.mEmail != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_EMAIL, this.mEmail));
- }
- if (this.mWebsite != null) {
- appInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_WEBSITE, this.mWebsite));
- }
return XmlUtils.listOf(appInfoEle);
}
@@ -147,54 +78,28 @@
@Override
public List<Element> toHrDomElements(Document doc) {
Element appInfoEle = doc.createElement(XmlUtils.HR_TAG_APP_INFO);
- if (this.mTitle != null) {
- appInfoEle.setAttribute(XmlUtils.HR_ATTR_TITLE, this.mTitle);
- }
- if (this.mDescription != null) {
- appInfoEle.setAttribute(XmlUtils.HR_ATTR_DESCRIPTION, this.mDescription);
- }
- if (this.mContainsAds != null) {
+ if (this.mApsCompliant != null) {
appInfoEle.setAttribute(
- XmlUtils.HR_ATTR_CONTAINS_ADS, String.valueOf(this.mContainsAds));
- }
- if (this.mObeyAps != null) {
- appInfoEle.setAttribute(XmlUtils.HR_ATTR_OBEY_APS, String.valueOf(this.mObeyAps));
- }
- if (this.mAdsFingerprinting != null) {
- appInfoEle.setAttribute(
- XmlUtils.HR_ATTR_ADS_FINGERPRINTING, String.valueOf(this.mAdsFingerprinting));
- }
- if (this.mSecurityFingerprinting != null) {
- appInfoEle.setAttribute(
- XmlUtils.HR_ATTR_SECURITY_FINGERPRINTING,
- String.valueOf(this.mSecurityFingerprinting));
+ XmlUtils.HR_ATTR_APS_COMPLIANT, String.valueOf(this.mApsCompliant));
}
if (this.mPrivacyPolicy != null) {
appInfoEle.setAttribute(XmlUtils.HR_ATTR_PRIVACY_POLICY, this.mPrivacyPolicy);
}
- if (this.mSecurityEndpoints != null) {
- appInfoEle.setAttribute(
- XmlUtils.HR_ATTR_SECURITY_ENDPOINTS, String.join("|", this.mSecurityEndpoints));
- }
+
if (this.mFirstPartyEndpoints != null) {
- appInfoEle.setAttribute(
- XmlUtils.HR_ATTR_FIRST_PARTY_ENDPOINTS,
- String.join("|", this.mFirstPartyEndpoints));
+ appInfoEle.appendChild(
+ XmlUtils.createHrArray(
+ doc, XmlUtils.HR_TAG_FIRST_PARTY_ENDPOINTS, mFirstPartyEndpoints));
}
+
if (this.mServiceProviderEndpoints != null) {
- appInfoEle.setAttribute(
- XmlUtils.HR_ATTR_SERVICE_PROVIDER_ENDPOINTS,
- String.join("|", this.mServiceProviderEndpoints));
+ appInfoEle.appendChild(
+ XmlUtils.createHrArray(
+ doc,
+ XmlUtils.HR_TAG_SERVICE_PROVIDER_ENDPOINTS,
+ mServiceProviderEndpoints));
}
- if (this.mCategory != null) {
- appInfoEle.setAttribute(XmlUtils.HR_ATTR_CATEGORY, this.mCategory);
- }
- if (this.mEmail != null) {
- appInfoEle.setAttribute(XmlUtils.HR_ATTR_EMAIL, this.mEmail);
- }
- if (this.mWebsite != null) {
- appInfoEle.setAttribute(XmlUtils.HR_ATTR_WEBSITE, this.mWebsite);
- }
+
return XmlUtils.listOf(appInfoEle);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
index c506961..6ad2027 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/AppInfoFactory.java
@@ -35,43 +35,19 @@
return null;
}
- String title = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_TITLE, true);
- String description = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_DESCRIPTION, true);
- Boolean containsAds = XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_CONTAINS_ADS, true);
- Boolean obeyAps = XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_OBEY_APS, true);
- Boolean adsFingerprinting =
- XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_ADS_FINGERPRINTING, true);
- Boolean securityFingerprinting =
- XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_SECURITY_FINGERPRINTING, true);
+ Boolean apsCompliant =
+ XmlUtils.getBoolAttr(appInfoEle, XmlUtils.HR_ATTR_APS_COMPLIANT, true);
String privacyPolicy =
XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_PRIVACY_POLICY, true);
- List<String> securityEndpoints =
- XmlUtils.getPipelineSplitAttr(
- appInfoEle, XmlUtils.HR_ATTR_SECURITY_ENDPOINTS, true);
List<String> firstPartyEndpoints =
- XmlUtils.getPipelineSplitAttr(
- appInfoEle, XmlUtils.HR_ATTR_FIRST_PARTY_ENDPOINTS, true);
+ XmlUtils.getHrItemsAsStrings(
+ appInfoEle, XmlUtils.HR_TAG_FIRST_PARTY_ENDPOINTS, true);
List<String> serviceProviderEndpoints =
- XmlUtils.getPipelineSplitAttr(
- appInfoEle, XmlUtils.HR_ATTR_SERVICE_PROVIDER_ENDPOINTS, true);
- String category = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_CATEGORY, true);
- String email = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_EMAIL, true);
- String website = XmlUtils.getStringAttr(appInfoEle, XmlUtils.HR_ATTR_WEBSITE, false);
+ XmlUtils.getHrItemsAsStrings(
+ appInfoEle, XmlUtils.HR_TAG_SERVICE_PROVIDER_ENDPOINTS, true);
return new AppInfo(
- title,
- description,
- containsAds,
- obeyAps,
- adsFingerprinting,
- securityFingerprinting,
- privacyPolicy,
- securityEndpoints,
- firstPartyEndpoints,
- serviceProviderEndpoints,
- category,
- email,
- website);
+ apsCompliant, privacyPolicy, firstPartyEndpoints, serviceProviderEndpoints);
}
/** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
@@ -83,42 +59,17 @@
return null;
}
- String title = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_TITLE, true);
- String description =
- XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_DESCRIPTION, true);
- Boolean containsAds =
- XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_CONTAINS_ADS, true);
- Boolean obeyAps = XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_OBEY_APS, true);
- Boolean adsFingerprinting =
- XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_ADS_FINGERPRINTING, true);
- Boolean securityFingerprinting =
- XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_SECURITY_FINGERPRINTING, true);
+ Boolean apsCompliant =
+ XmlUtils.getOdBoolEle(appInfoEle, XmlUtils.OD_NAME_APS_COMPLIANT, true);
String privacyPolicy =
XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_PRIVACY_POLICY, true);
- List<String> securityEndpoints =
- XmlUtils.getOdStringArray(appInfoEle, XmlUtils.OD_NAME_SECURITY_ENDPOINT, true);
List<String> firstPartyEndpoints =
- XmlUtils.getOdStringArray(appInfoEle, XmlUtils.OD_NAME_FIRST_PARTY_ENDPOINT, true);
+ XmlUtils.getOdStringArray(appInfoEle, XmlUtils.OD_NAME_FIRST_PARTY_ENDPOINTS, true);
List<String> serviceProviderEndpoints =
XmlUtils.getOdStringArray(
- appInfoEle, XmlUtils.OD_NAME_SERVICE_PROVIDER_ENDPOINT, true);
- String category = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_CATEGORY, true);
- String email = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_EMAIL, true);
- String website = XmlUtils.getOdStringEle(appInfoEle, XmlUtils.OD_NAME_WEBSITE, false);
+ appInfoEle, XmlUtils.OD_NAME_SERVICE_PROVIDER_ENDPOINTS, true);
return new AppInfo(
- title,
- description,
- containsAds,
- obeyAps,
- adsFingerprinting,
- securityFingerprinting,
- privacyPolicy,
- securityEndpoints,
- firstPartyEndpoints,
- serviceProviderEndpoints,
- category,
- email,
- website);
+ apsCompliant, privacyPolicy, firstPartyEndpoints, serviceProviderEndpoints);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
index ba0e3db..3c93c88 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
/**
* Data label representation with data shared and data collected maps containing zero or more {@link
@@ -138,7 +139,7 @@
"|",
dataType.getPurposes().stream()
.map(DataType.Purpose::toString)
- .toList()));
+ .collect(Collectors.toList())));
dataLabelsEle.appendChild(hrDataTypeEle);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
index d2326d1..284a4b8 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataType.java
@@ -25,6 +25,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Data usage type representation. Types are specific to a {@link DataCategory} and contains
@@ -182,7 +183,7 @@
XmlUtils.OD_NAME_PURPOSES,
this.getPurposes().stream()
.map(p -> String.valueOf(p.getValue()))
- .toList()));
+ .collect(Collectors.toList())));
}
maybeAddBoolToOdElement(
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java
deleted file mode 100644
index 94fad96..0000000
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfo.java
+++ /dev/null
@@ -1,173 +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.asllib.marshallable;
-
-import com.android.asllib.util.XmlUtils;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.util.List;
-
-/** DeveloperInfo representation */
-public class DeveloperInfo implements AslMarshallable {
- public enum DeveloperRelationship {
- OEM(0),
- ODM(1),
- SOC(2),
- OTA(3),
- CARRIER(4),
- AOSP(5),
- OTHER(6);
-
- private final int mValue;
-
- DeveloperRelationship(int value) {
- this.mValue = value;
- }
-
- /** Get the int value associated with the DeveloperRelationship. */
- public int getValue() {
- return mValue;
- }
-
- /** Get the DeveloperRelationship associated with the int value. */
- public static DeveloperInfo.DeveloperRelationship forValue(int value) {
- for (DeveloperInfo.DeveloperRelationship e : values()) {
- if (e.getValue() == value) {
- return e;
- }
- }
- throw new IllegalArgumentException("No DeveloperRelationship enum for value: " + value);
- }
-
- /** Get the DeveloperRelationship associated with the human-readable String. */
- public static DeveloperInfo.DeveloperRelationship forString(String s) {
- for (DeveloperInfo.DeveloperRelationship e : values()) {
- if (e.toString().equals(s)) {
- return e;
- }
- }
- throw new IllegalArgumentException("No DeveloperRelationship enum for str: " + s);
- }
-
- /** Human-readable String representation of DeveloperRelationship. */
- public String toString() {
- return this.name().toLowerCase();
- }
- }
-
- private final String mName;
- private final String mEmail;
- private final String mAddress;
- private final String mCountryRegion;
- private final DeveloperRelationship mDeveloperRelationship;
- private final String mWebsite;
- private final String mAppDeveloperRegistryId;
-
- public DeveloperInfo(
- String name,
- String email,
- String address,
- String countryRegion,
- DeveloperRelationship developerRelationship,
- String website,
- String appDeveloperRegistryId) {
- this.mName = name;
- this.mEmail = email;
- this.mAddress = address;
- this.mCountryRegion = countryRegion;
- this.mDeveloperRelationship = developerRelationship;
- this.mWebsite = website;
- this.mAppDeveloperRegistryId = appDeveloperRegistryId;
- }
-
- /** Creates an on-device DOM element from the {@link SafetyLabels}. */
- @Override
- public List<Element> toOdDomElements(Document doc) {
- Element developerInfoEle =
- XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_DEVELOPER_INFO);
- if (mName != null) {
- developerInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_NAME, mName));
- }
- if (mEmail != null) {
- developerInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_EMAIL, mEmail));
- }
- if (mAddress != null) {
- developerInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_ADDRESS, mAddress));
- }
- if (mCountryRegion != null) {
- developerInfoEle.appendChild(
- XmlUtils.createOdStringEle(
- doc, XmlUtils.OD_NAME_COUNTRY_REGION, mCountryRegion));
- }
- if (mDeveloperRelationship != null) {
- developerInfoEle.appendChild(
- XmlUtils.createOdLongEle(
- doc,
- XmlUtils.OD_NAME_DEVELOPER_RELATIONSHIP,
- mDeveloperRelationship.getValue()));
- }
- if (mWebsite != null) {
- developerInfoEle.appendChild(
- XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_WEBSITE, mWebsite));
- }
- if (mAppDeveloperRegistryId != null) {
- developerInfoEle.appendChild(
- XmlUtils.createOdStringEle(
- doc,
- XmlUtils.OD_NAME_APP_DEVELOPER_REGISTRY_ID,
- mAppDeveloperRegistryId));
- }
-
- return XmlUtils.listOf(developerInfoEle);
- }
-
- /** Creates the human-readable DOM elements from the AslMarshallable Java Object. */
- @Override
- public List<Element> toHrDomElements(Document doc) {
- Element developerInfoEle = doc.createElement(XmlUtils.HR_TAG_DEVELOPER_INFO);
- if (mName != null) {
- developerInfoEle.setAttribute(XmlUtils.HR_ATTR_NAME, mName);
- }
- if (mEmail != null) {
- developerInfoEle.setAttribute(XmlUtils.HR_ATTR_EMAIL, mEmail);
- }
- if (mAddress != null) {
- developerInfoEle.setAttribute(XmlUtils.HR_ATTR_ADDRESS, mAddress);
- }
- if (mCountryRegion != null) {
- developerInfoEle.setAttribute(XmlUtils.HR_ATTR_COUNTRY_REGION, mCountryRegion);
- }
- if (mDeveloperRelationship != null) {
- developerInfoEle.setAttribute(
- XmlUtils.HR_ATTR_DEVELOPER_RELATIONSHIP, mDeveloperRelationship.toString());
- }
- if (mWebsite != null) {
- developerInfoEle.setAttribute(XmlUtils.HR_ATTR_WEBSITE, mWebsite);
- }
- if (mAppDeveloperRegistryId != null) {
- developerInfoEle.setAttribute(
- XmlUtils.HR_ATTR_APP_DEVELOPER_REGISTRY_ID, mAppDeveloperRegistryId);
- }
-
- return XmlUtils.listOf(developerInfoEle);
- }
-}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java
deleted file mode 100644
index 0f3b41c..0000000
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DeveloperInfoFactory.java
+++ /dev/null
@@ -1,96 +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.asllib.marshallable;
-
-import com.android.asllib.util.AslgenUtil;
-import com.android.asllib.util.MalformedXmlException;
-import com.android.asllib.util.XmlUtils;
-
-import org.w3c.dom.Element;
-
-import java.util.List;
-
-public class DeveloperInfoFactory implements AslMarshallableFactory<DeveloperInfo> {
-
- /** Creates a {@link DeveloperInfo} from the human-readable DOM element. */
- @Override
- public DeveloperInfo createFromHrElements(List<Element> elements) throws MalformedXmlException {
- Element developerInfoEle = XmlUtils.getSingleElement(elements);
- if (developerInfoEle == null) {
- AslgenUtil.logI("No DeveloperInfo found in hr format.");
- return null;
- }
- String name = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_NAME, true);
- String email = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_EMAIL, true);
- String address = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_ADDRESS, true);
- String countryRegion =
- XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_COUNTRY_REGION, true);
- DeveloperInfo.DeveloperRelationship developerRelationship =
- DeveloperInfo.DeveloperRelationship.forString(
- XmlUtils.getStringAttr(
- developerInfoEle, XmlUtils.HR_ATTR_DEVELOPER_RELATIONSHIP, true));
- String website = XmlUtils.getStringAttr(developerInfoEle, XmlUtils.HR_ATTR_WEBSITE, false);
- String appDeveloperRegistryId =
- XmlUtils.getStringAttr(
- developerInfoEle, XmlUtils.HR_ATTR_APP_DEVELOPER_REGISTRY_ID, false);
-
- return new DeveloperInfo(
- name,
- email,
- address,
- countryRegion,
- developerRelationship,
- website,
- appDeveloperRegistryId);
- }
-
- /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
- @Override
- public DeveloperInfo createFromOdElements(List<Element> elements) throws MalformedXmlException {
- Element developerInfoEle = XmlUtils.getSingleElement(elements);
- if (developerInfoEle == null) {
- AslgenUtil.logI("No DeveloperInfo found in od format.");
- return null;
- }
- String name = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_NAME, true);
- String email = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_EMAIL, true);
- String address = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_ADDRESS, true);
- String countryRegion =
- XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_COUNTRY_REGION, true);
- DeveloperInfo.DeveloperRelationship developerRelationship =
- DeveloperInfo.DeveloperRelationship.forValue(
- (int)
- (long)
- XmlUtils.getOdLongEle(
- developerInfoEle,
- XmlUtils.OD_NAME_DEVELOPER_RELATIONSHIP,
- true));
- String website = XmlUtils.getOdStringEle(developerInfoEle, XmlUtils.OD_NAME_WEBSITE, false);
- String appDeveloperRegistryId =
- XmlUtils.getOdStringEle(
- developerInfoEle, XmlUtils.OD_NAME_APP_DEVELOPER_REGISTRY_ID, false);
-
- return new DeveloperInfo(
- name,
- email,
- address,
- countryRegion,
- developerRelationship,
- website,
- appDeveloperRegistryId);
- }
-}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
index 6af8071..2a4e130 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabels.java
@@ -25,21 +25,10 @@
/** Safety Label representation containing zero or more {@link DataCategory} for data shared */
public class SafetyLabels implements AslMarshallable {
-
- private final Long mVersion;
private final DataLabels mDataLabels;
- private final SecurityLabels mSecurityLabels;
- private final ThirdPartyVerification mThirdPartyVerification;
- public SafetyLabels(
- Long version,
- DataLabels dataLabels,
- SecurityLabels securityLabels,
- ThirdPartyVerification thirdPartyVerification) {
- this.mVersion = version;
+ public SafetyLabels(DataLabels dataLabels) {
this.mDataLabels = dataLabels;
- this.mSecurityLabels = securityLabels;
- this.mThirdPartyVerification = thirdPartyVerification;
}
/** Returns the data label for the safety label */
@@ -47,27 +36,14 @@
return mDataLabels;
}
- /** Gets the version of the {@link SafetyLabels}. */
- public Long getVersion() {
- return mVersion;
- }
-
/** Creates an on-device DOM element from the {@link SafetyLabels}. */
@Override
public List<Element> toOdDomElements(Document doc) {
Element safetyLabelsEle =
XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_SAFETY_LABELS);
- safetyLabelsEle.appendChild(
- XmlUtils.createOdLongEle(doc, XmlUtils.OD_NAME_VERSION, mVersion));
if (mDataLabels != null) {
XmlUtils.appendChildren(safetyLabelsEle, mDataLabels.toOdDomElements(doc));
}
- if (mSecurityLabels != null) {
- XmlUtils.appendChildren(safetyLabelsEle, mSecurityLabels.toOdDomElements(doc));
- }
- if (mThirdPartyVerification != null) {
- XmlUtils.appendChildren(safetyLabelsEle, mThirdPartyVerification.toOdDomElements(doc));
- }
return XmlUtils.listOf(safetyLabelsEle);
}
@@ -75,17 +51,10 @@
@Override
public List<Element> toHrDomElements(Document doc) {
Element safetyLabelsEle = doc.createElement(XmlUtils.HR_TAG_SAFETY_LABELS);
- safetyLabelsEle.setAttribute(XmlUtils.HR_ATTR_VERSION, String.valueOf(mVersion));
if (mDataLabels != null) {
XmlUtils.appendChildren(safetyLabelsEle, mDataLabels.toHrDomElements(doc));
}
- if (mSecurityLabels != null) {
- XmlUtils.appendChildren(safetyLabelsEle, mSecurityLabels.toHrDomElements(doc));
- }
- if (mThirdPartyVerification != null) {
- XmlUtils.appendChildren(safetyLabelsEle, mThirdPartyVerification.toHrDomElements(doc));
- }
return XmlUtils.listOf(safetyLabelsEle);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
index 2644b43..2738337 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SafetyLabelsFactory.java
@@ -34,7 +34,6 @@
AslgenUtil.logI("No SafetyLabels found in hr format.");
return null;
}
- long version = XmlUtils.tryGetVersion(safetyLabelsEle);
DataLabels dataLabels =
new DataLabelsFactory()
@@ -44,23 +43,7 @@
safetyLabelsEle,
XmlUtils.HR_TAG_DATA_LABELS,
false)));
- SecurityLabels securityLabels =
- new SecurityLabelsFactory()
- .createFromHrElements(
- XmlUtils.listOf(
- XmlUtils.getSingleChildElement(
- safetyLabelsEle,
- XmlUtils.HR_TAG_SECURITY_LABELS,
- false)));
- ThirdPartyVerification thirdPartyVerification =
- new ThirdPartyVerificationFactory()
- .createFromHrElements(
- XmlUtils.listOf(
- XmlUtils.getSingleChildElement(
- safetyLabelsEle,
- XmlUtils.HR_TAG_THIRD_PARTY_VERIFICATION,
- false)));
- return new SafetyLabels(version, dataLabels, securityLabels, thirdPartyVerification);
+ return new SafetyLabels(dataLabels);
}
/** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
@@ -71,7 +54,6 @@
AslgenUtil.logI("No SafetyLabels found in od format.");
return null;
}
- Long version = XmlUtils.getOdLongEle(safetyLabelsEle, XmlUtils.OD_NAME_VERSION, true);
DataLabels dataLabels =
new DataLabelsFactory()
@@ -81,22 +63,7 @@
safetyLabelsEle,
XmlUtils.OD_NAME_DATA_LABELS,
false)));
- SecurityLabels securityLabels =
- new SecurityLabelsFactory()
- .createFromOdElements(
- XmlUtils.listOf(
- XmlUtils.getOdPbundleWithName(
- safetyLabelsEle,
- XmlUtils.OD_NAME_SECURITY_LABELS,
- false)));
- ThirdPartyVerification thirdPartyVerification =
- new ThirdPartyVerificationFactory()
- .createFromOdElements(
- XmlUtils.listOf(
- XmlUtils.getOdPbundleWithName(
- safetyLabelsEle,
- XmlUtils.OD_NAME_THIRD_PARTY_VERIFICATION,
- false)));
- return new SafetyLabels(version, dataLabels, securityLabels, thirdPartyVerification);
+
+ return new SafetyLabels(dataLabels);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
index 6a8700a..9f789f0 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfo.java
@@ -23,22 +23,14 @@
import java.util.List;
-/** TransparencyInfo representation containing {@link DeveloperInfo} and {@link AppInfo} */
+/** TransparencyInfo representation containing {@link AppInfo} */
public class TransparencyInfo implements AslMarshallable {
-
- private final DeveloperInfo mDeveloperInfo;
private final AppInfo mAppInfo;
- public TransparencyInfo(DeveloperInfo developerInfo, AppInfo appInfo) {
- this.mDeveloperInfo = developerInfo;
+ public TransparencyInfo(AppInfo appInfo) {
this.mAppInfo = appInfo;
}
- /** Gets the {@link DeveloperInfo} of the {@link TransparencyInfo}. */
- public DeveloperInfo getDeveloperInfo() {
- return mDeveloperInfo;
- }
-
/** Gets the {@link AppInfo} of the {@link TransparencyInfo}. */
public AppInfo getAppInfo() {
return mAppInfo;
@@ -49,9 +41,6 @@
public List<Element> toOdDomElements(Document doc) {
Element transparencyInfoEle =
XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_TRANSPARENCY_INFO);
- if (mDeveloperInfo != null) {
- XmlUtils.appendChildren(transparencyInfoEle, mDeveloperInfo.toOdDomElements(doc));
- }
if (mAppInfo != null) {
XmlUtils.appendChildren(transparencyInfoEle, mAppInfo.toOdDomElements(doc));
}
@@ -62,9 +51,6 @@
@Override
public List<Element> toHrDomElements(Document doc) {
Element transparencyInfoEle = doc.createElement(XmlUtils.HR_TAG_TRANSPARENCY_INFO);
- if (mDeveloperInfo != null) {
- XmlUtils.appendChildren(transparencyInfoEle, mDeveloperInfo.toHrDomElements(doc));
- }
if (mAppInfo != null) {
XmlUtils.appendChildren(transparencyInfoEle, mAppInfo.toHrDomElements(doc));
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
index 94c5640..40f2872 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/TransparencyInfoFactory.java
@@ -36,18 +36,11 @@
return null;
}
- Element developerInfoEle =
- XmlUtils.getSingleChildElement(
- transparencyInfoEle, XmlUtils.HR_TAG_DEVELOPER_INFO, false);
- DeveloperInfo developerInfo =
- new DeveloperInfoFactory().createFromHrElements(XmlUtils.listOf(developerInfoEle));
-
Element appInfoEle =
- XmlUtils.getSingleChildElement(
- transparencyInfoEle, XmlUtils.HR_TAG_APP_INFO, false);
+ XmlUtils.getSingleChildElement(transparencyInfoEle, XmlUtils.HR_TAG_APP_INFO, true);
AppInfo appInfo = new AppInfoFactory().createFromHrElements(XmlUtils.listOf(appInfoEle));
- return new TransparencyInfo(developerInfo, appInfo);
+ return new TransparencyInfo(appInfo);
}
/** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
@@ -60,17 +53,10 @@
return null;
}
- Element developerInfoEle =
- XmlUtils.getOdPbundleWithName(
- transparencyInfoEle, XmlUtils.OD_NAME_DEVELOPER_INFO, false);
- DeveloperInfo developerInfo =
- new DeveloperInfoFactory().createFromOdElements(XmlUtils.listOf(developerInfoEle));
-
Element appInfoEle =
- XmlUtils.getOdPbundleWithName(
- transparencyInfoEle, XmlUtils.OD_NAME_APP_INFO, false);
+ XmlUtils.getOdPbundleWithName(transparencyInfoEle, XmlUtils.OD_NAME_APP_INFO, true);
AppInfo appInfo = new AppInfoFactory().createFromOdElements(XmlUtils.listOf(appInfoEle));
- return new TransparencyInfo(developerInfo, appInfo);
+ return new TransparencyInfo(appInfo);
}
}
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
index 97cbc39..2c1517b 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
@@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
public class XmlUtils {
public static final String DATA_TYPE_SEPARATOR = "_data_type_";
@@ -42,6 +43,7 @@
public static final String HR_TAG_DATA_COLLECTED = "data-collected";
public static final String HR_TAG_DATA_COLLECTED_EPHEMERAL = "data-collected-ephemeral";
public static final String HR_TAG_DATA_SHARED = "data-shared";
+ public static final String HR_TAG_ITEM = "item";
public static final String HR_ATTR_NAME = "name";
public static final String HR_ATTR_EMAIL = "email";
public static final String HR_ATTR_ADDRESS = "address";
@@ -64,12 +66,13 @@
public static final String HR_ATTR_DESCRIPTION = "description";
public static final String HR_ATTR_CONTAINS_ADS = "containsAds";
public static final String HR_ATTR_OBEY_APS = "obeyAps";
+ public static final String HR_ATTR_APS_COMPLIANT = "apsCompliant";
public static final String HR_ATTR_ADS_FINGERPRINTING = "adsFingerprinting";
public static final String HR_ATTR_SECURITY_FINGERPRINTING = "securityFingerprinting";
public static final String HR_ATTR_PRIVACY_POLICY = "privacyPolicy";
public static final String HR_ATTR_SECURITY_ENDPOINTS = "securityEndpoints";
- public static final String HR_ATTR_FIRST_PARTY_ENDPOINTS = "firstPartyEndpoints";
- public static final String HR_ATTR_SERVICE_PROVIDER_ENDPOINTS = "serviceProviderEndpoints";
+ public static final String HR_TAG_FIRST_PARTY_ENDPOINTS = "first-party-endpoints";
+ public static final String HR_TAG_SERVICE_PROVIDER_ENDPOINTS = "service-provider-endpoints";
public static final String HR_ATTR_CATEGORY = "category";
public static final String OD_TAG_BUNDLE = "bundle";
@@ -98,12 +101,13 @@
public static final String OD_NAME_DESCRIPTION = "description";
public static final String OD_NAME_CONTAINS_ADS = "contains_ads";
public static final String OD_NAME_OBEY_APS = "obey_aps";
+ public static final String OD_NAME_APS_COMPLIANT = "aps_compliant";
public static final String OD_NAME_ADS_FINGERPRINTING = "ads_fingerprinting";
public static final String OD_NAME_SECURITY_FINGERPRINTING = "security_fingerprinting";
public static final String OD_NAME_PRIVACY_POLICY = "privacy_policy";
- public static final String OD_NAME_SECURITY_ENDPOINT = "security_endpoint";
- public static final String OD_NAME_FIRST_PARTY_ENDPOINT = "first_party_endpoint";
- public static final String OD_NAME_SERVICE_PROVIDER_ENDPOINT = "service_provider_endpoint";
+ public static final String OD_NAME_SECURITY_ENDPOINT = "security_endpoints";
+ public static final String OD_NAME_FIRST_PARTY_ENDPOINTS = "first_party_endpoints";
+ public static final String OD_NAME_SERVICE_PROVIDER_ENDPOINTS = "service_provider_endpoints";
public static final String OD_NAME_CATEGORY = "category";
public static final String OD_NAME_VERSION = "version";
public static final String OD_NAME_URL = "url";
@@ -128,7 +132,9 @@
/** Gets the top-level children with the tag name.. */
public static List<Element> getChildrenByTagName(Node parentEle, String tagName) {
var elements = XmlUtils.asElementList(parentEle.getChildNodes());
- return elements.stream().filter(e -> e.getTagName().equals(tagName)).toList();
+ return elements.stream()
+ .filter(e -> e.getTagName().equals(tagName))
+ .collect(Collectors.toList());
}
/**
@@ -237,7 +243,18 @@
return ele;
}
- /** Create OD style array DOM Element, which can represent any time but is stored as Strings. */
+ /** Create HR style array DOM Element. */
+ public static Element createHrArray(Document doc, String arrayTagName, List<String> arrayVals) {
+ Element arrEle = doc.createElement(arrayTagName);
+ for (String s : arrayVals) {
+ Element itemEle = doc.createElement(XmlUtils.HR_TAG_ITEM);
+ itemEle.setTextContent(s);
+ arrEle.appendChild(itemEle);
+ }
+ return arrEle;
+ }
+
+ /** Create OD style array DOM Element, which can represent any type but is stored as Strings. */
public static Element createOdArray(
Document doc, String arrayTag, String arrayName, List<String> arrayVals) {
Element arrEle = doc.createElement(arrayTag);
@@ -272,7 +289,8 @@
/** Gets a pipeline-split attribute. */
public static List<String> getPipelineSplitAttr(Element ele, String attrName, boolean required)
throws MalformedXmlException {
- List<String> list = Arrays.stream(ele.getAttribute(attrName).split("\\|")).toList();
+ List<String> list =
+ Arrays.stream(ele.getAttribute(attrName).split("\\|")).collect(Collectors.toList());
if ((list.isEmpty() || list.get(0).isEmpty()) && required) {
throw new MalformedXmlException(
String.format(
@@ -301,7 +319,7 @@
List<Element> boolEles =
XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_BOOLEAN).stream()
.filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
- .toList();
+ .collect(Collectors.toList());
if (boolEles.size() > 1) {
throw new MalformedXmlException(
String.format(
@@ -332,7 +350,7 @@
List<Element> longEles =
XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_LONG).stream()
.filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
- .toList();
+ .collect(Collectors.toList());
if (longEles.size() > 1) {
throw new MalformedXmlException(
String.format(
@@ -363,7 +381,7 @@
List<Element> eles =
XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_STRING).stream()
.filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
- .toList();
+ .collect(Collectors.toList());
if (eles.size() > 1) {
throw new MalformedXmlException(
String.format(
@@ -391,7 +409,7 @@
List<Element> eles =
XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_PBUNDLE_AS_MAP).stream()
.filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
- .toList();
+ .collect(Collectors.toList());
if (eles.size() > 1) {
throw new MalformedXmlException(
String.format(
@@ -435,7 +453,7 @@
List<Element> intArrayEles =
XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_INT_ARRAY).stream()
.filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
- .toList();
+ .collect(Collectors.toList());
if (intArrayEles.size() > 1) {
throw new MalformedXmlException(
String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
@@ -456,13 +474,39 @@
return ints;
}
+ /** Gets human-readable style String array. */
+ public static List<String> getHrItemsAsStrings(
+ Element parent, String elementName, boolean required) throws MalformedXmlException {
+
+ List<Element> arrayEles = XmlUtils.getChildrenByTagName(parent, elementName);
+ if (arrayEles.size() > 1) {
+ throw new MalformedXmlException(
+ String.format(
+ "Found more than one %s in %s.", elementName, parent.getTagName()));
+ }
+ if (arrayEles.isEmpty()) {
+ if (required) {
+ throw new MalformedXmlException(
+ String.format("Found no %s in %s.", elementName, parent.getTagName()));
+ }
+ return null;
+ }
+ Element arrayEle = arrayEles.get(0);
+ List<Element> itemEles = XmlUtils.getChildrenByTagName(arrayEle, XmlUtils.HR_TAG_ITEM);
+ List<String> strs = new ArrayList<String>();
+ for (Element itemEle : itemEles) {
+ strs.add(itemEle.getTextContent());
+ }
+ return strs;
+ }
+
/** Gets on-device style String array. */
public static List<String> getOdStringArray(Element ele, String nameName, boolean required)
throws MalformedXmlException {
List<Element> arrayEles =
XmlUtils.getChildrenByTagName(ele, XmlUtils.OD_TAG_STRING_ARRAY).stream()
.filter(e -> e.getAttribute(XmlUtils.OD_ATTR_NAME).equals(nameName))
- .toList();
+ .collect(Collectors.toList());
if (arrayEles.size() > 1) {
throw new MalformedXmlException(
String.format(
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
index dbeeb49..14e65e5 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
@@ -20,11 +20,8 @@
import com.android.asllib.marshallable.AppInfoTest;
import com.android.asllib.marshallable.DataLabelsTest;
import com.android.asllib.marshallable.DataTypeEqualityTest;
-import com.android.asllib.marshallable.DeveloperInfoTest;
import com.android.asllib.marshallable.SafetyLabelsTest;
-import com.android.asllib.marshallable.SecurityLabelsTest;
import com.android.asllib.marshallable.SystemAppSafetyLabelTest;
-import com.android.asllib.marshallable.ThirdPartyVerificationTest;
import com.android.asllib.marshallable.TransparencyInfoTest;
import org.junit.runner.RunWith;
@@ -35,14 +32,10 @@
AslgenTests.class,
AndroidSafetyLabelTest.class,
AppInfoTest.class,
- // DataCategoryTest.class,
DataLabelsTest.class,
DataTypeEqualityTest.class,
- DeveloperInfoTest.class,
SafetyLabelsTest.class,
- SecurityLabelsTest.class,
SystemAppSafetyLabelTest.class,
- ThirdPartyVerificationTest.class,
TransparencyInfoTest.class
})
public class AllTests {}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
index 9e91c6f..d823c48 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java
@@ -20,6 +20,7 @@
import com.android.asllib.testutils.TestUtils;
import com.android.asllib.util.MalformedXmlException;
+import com.android.asllib.util.XmlUtils;
import org.junit.Before;
import org.junit.Test;
@@ -34,35 +35,15 @@
private static final String APP_INFO_HR_PATH = "com/android/asllib/appinfo/hr";
private static final String APP_INFO_OD_PATH = "com/android/asllib/appinfo/od";
public static final List<String> REQUIRED_FIELD_NAMES =
- List.of(
- "title",
- "description",
- "containsAds",
- "obeyAps",
- "adsFingerprinting",
- "securityFingerprinting",
- "privacyPolicy",
- "securityEndpoints",
- "firstPartyEndpoints",
- "serviceProviderEndpoints",
- "category",
- "email");
+ List.of("apsCompliant", "privacyPolicy");
public static final List<String> REQUIRED_FIELD_NAMES_OD =
- List.of(
- "title",
- "description",
- "contains_ads",
- "obey_aps",
- "ads_fingerprinting",
- "security_fingerprinting",
- "privacy_policy",
- "security_endpoint",
- "first_party_endpoint",
- "service_provider_endpoint",
- "category",
- "email");
- public static final List<String> OPTIONAL_FIELD_NAMES = List.of("website");
- public static final List<String> OPTIONAL_FIELD_NAMES_OD = List.of("website");
+ List.of("aps_compliant", "privacy_policy");
+ public static final List<String> REQUIRED_CHILD_NAMES =
+ List.of("first-party-endpoints", "service-provider-endpoints");
+ public static final List<String> REQUIRED_CHILD_NAMES_OD =
+ List.of("first_party_endpoints", "service_provider_endpoints");
+ public static final List<String> OPTIONAL_FIELD_NAMES = List.of();
+ public static final List<String> OPTIONAL_FIELD_NAMES_OD = List.of();
private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
@@ -110,6 +91,34 @@
}
}
+ /** Tests missing required child fails. */
+ @Test
+ public void testMissingRequiredChild() throws Exception {
+ System.out.println("Starting testMissingRequiredFields");
+ for (String reqChildName : REQUIRED_CHILD_NAMES) {
+ System.out.println("testing missing required child hr: " + reqChildName);
+ var appInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ var child = XmlUtils.getChildrenByTagName(appInfoEle.get(0), reqChildName).get(0);
+ appInfoEle.get(0).removeChild(child);
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new AppInfoFactory().createFromHrElements(appInfoEle));
+ }
+
+ for (String reqField : REQUIRED_CHILD_NAMES_OD) {
+ System.out.println("testing missing required child od: " + reqField);
+ var appInfoEle =
+ TestUtils.getElementsFromResource(
+ Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
+ TestUtils.removeOdChildEleWithName(appInfoEle.get(0), reqField);
+ assertThrows(
+ MalformedXmlException.class,
+ () -> new AppInfoFactory().createFromOdElements(appInfoEle));
+ }
+ }
+
/** Tests missing optional fields passes. */
@Test
public void testMissingOptionalFields() throws Exception {
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
deleted file mode 100644
index 72e8d65..0000000
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 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.asllib.marshallable;
-
-import static org.junit.Assert.assertThrows;
-
-import com.android.asllib.testutils.TestUtils;
-import com.android.asllib.util.MalformedXmlException;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.nio.file.Paths;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class DeveloperInfoTest {
- private static final String DEVELOPER_INFO_HR_PATH = "com/android/asllib/developerinfo/hr";
- private static final String DEVELOPER_INFO_OD_PATH = "com/android/asllib/developerinfo/od";
- public static final List<String> REQUIRED_FIELD_NAMES =
- List.of("address", "countryRegion", "email", "name", "relationship");
- public static final List<String> REQUIRED_FIELD_NAMES_OD =
- List.of("address", "country_region", "email", "name", "relationship");
- public static final List<String> OPTIONAL_FIELD_NAMES = List.of("website", "registryId");
- public static final List<String> OPTIONAL_FIELD_NAMES_OD =
- List.of("website", "app_developer_registry_id");
-
- private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
-
- /** Logic for setting up tests (empty if not yet needed). */
- public static void main(String[] params) throws Exception {}
-
- @Before
- public void setUp() throws Exception {
- System.out.println("set up.");
- }
-
- /** Test for all fields valid. */
- @Test
- public void testAllFieldsValid() throws Exception {
- System.out.println("starting testAllFieldsValid.");
- testHrToOdDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME);
- testOdToHrDeveloperInfo(ALL_FIELDS_VALID_FILE_NAME);
- }
-
- /** Tests missing required fields fails. */
- @Test
- public void testMissingRequiredFields() throws Exception {
- System.out.println("Starting testMissingRequiredFields");
- for (String reqField : REQUIRED_FIELD_NAMES) {
- System.out.println("testing missing required field: " + reqField);
- var developerInfoEle =
- TestUtils.getElementsFromResource(
- Paths.get(DEVELOPER_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
- developerInfoEle.get(0).removeAttribute(reqField);
-
- assertThrows(
- MalformedXmlException.class,
- () -> new DeveloperInfoFactory().createFromHrElements(developerInfoEle));
- }
-
- for (String reqField : REQUIRED_FIELD_NAMES_OD) {
- System.out.println("testing missing required field od: " + reqField);
- var developerInfoEle =
- TestUtils.getElementsFromResource(
- Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
- TestUtils.removeOdChildEleWithName(developerInfoEle.get(0), reqField);
-
- assertThrows(
- MalformedXmlException.class,
- () -> new DeveloperInfoFactory().createFromOdElements(developerInfoEle));
- }
- }
-
- /** Tests missing optional fields passes. */
- @Test
- public void testMissingOptionalFields() throws Exception {
- for (String optField : OPTIONAL_FIELD_NAMES) {
- var developerInfoEle =
- TestUtils.getElementsFromResource(
- Paths.get(DEVELOPER_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
- developerInfoEle.get(0).removeAttribute(optField);
- DeveloperInfo developerInfo =
- new DeveloperInfoFactory().createFromHrElements(developerInfoEle);
- developerInfo.toOdDomElements(TestUtils.document());
- }
-
- for (String optField : OPTIONAL_FIELD_NAMES_OD) {
- var developerInfoEle =
- TestUtils.getElementsFromResource(
- Paths.get(DEVELOPER_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
- TestUtils.removeOdChildEleWithName(developerInfoEle.get(0), optField);
- DeveloperInfo developerInfo =
- new DeveloperInfoFactory().createFromOdElements(developerInfoEle);
- developerInfo.toHrDomElements(TestUtils.document());
- }
- }
-
- private void testHrToOdDeveloperInfo(String fileName) throws Exception {
- TestUtils.testHrToOd(
- TestUtils.document(),
- new DeveloperInfoFactory(),
- DEVELOPER_INFO_HR_PATH,
- DEVELOPER_INFO_OD_PATH,
- fileName);
- }
-
- private void testOdToHrDeveloperInfo(String fileName) throws Exception {
- TestUtils.testOdToHr(
- TestUtils.document(),
- new DeveloperInfoFactory(),
- DEVELOPER_INFO_OD_PATH,
- DEVELOPER_INFO_HR_PATH,
- fileName);
- }
-}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
index bba6b54..19d1626 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SafetyLabelsTest.java
@@ -28,26 +28,14 @@
private static final String SAFETY_LABELS_HR_PATH = "com/android/asllib/safetylabels/hr";
private static final String SAFETY_LABELS_OD_PATH = "com/android/asllib/safetylabels/od";
- private static final String MISSING_VERSION_FILE_NAME = "missing-version.xml";
private static final String VALID_EMPTY_FILE_NAME = "valid-empty.xml";
private static final String WITH_DATA_LABELS_FILE_NAME = "with-data-labels.xml";
- private static final String WITH_SECURITY_LABELS_FILE_NAME = "with-security-labels.xml";
- private static final String WITH_THIRD_PARTY_VERIFICATION_FILE_NAME =
- "with-third-party-verification.xml";
@Before
public void setUp() throws Exception {
System.out.println("set up.");
}
- /** Test for safety labels missing version. */
- @Test
- public void testSafetyLabelsMissingVersion() throws Exception {
- System.out.println("starting testSafetyLabelsMissingVersion.");
- hrToOdExpectException(MISSING_VERSION_FILE_NAME);
- odToHrExpectException(MISSING_VERSION_FILE_NAME);
- }
-
/** Test for safety labels valid empty. */
@Test
public void testSafetyLabelsValidEmptyFile() throws Exception {
@@ -64,22 +52,6 @@
testOdToHrSafetyLabels(WITH_DATA_LABELS_FILE_NAME);
}
- /** Test for safety labels with security labels. */
- @Test
- public void testSafetyLabelsWithSecurityLabels() throws Exception {
- System.out.println("starting testSafetyLabelsWithSecurityLabels.");
- testHrToOdSafetyLabels(WITH_SECURITY_LABELS_FILE_NAME);
- testOdToHrSafetyLabels(WITH_SECURITY_LABELS_FILE_NAME);
- }
-
- /** Test for safety labels with third party verification. */
- @Test
- public void testSafetyLabelsWithThirdPartyVerification() throws Exception {
- System.out.println("starting testSafetyLabelsWithThirdPartyVerification.");
- testHrToOdSafetyLabels(WITH_THIRD_PARTY_VERIFICATION_FILE_NAME);
- testOdToHrSafetyLabels(WITH_THIRD_PARTY_VERIFICATION_FILE_NAME);
- }
-
private void hrToOdExpectException(String fileName) {
TestUtils.hrToOdExpectException(new SafetyLabelsFactory(), SAFETY_LABELS_HR_PATH, fileName);
}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
deleted file mode 100644
index a940bc6..0000000
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 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.asllib.marshallable;
-
-
-import com.android.asllib.testutils.TestUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.nio.file.Paths;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class SecurityLabelsTest {
- private static final String SECURITY_LABELS_HR_PATH = "com/android/asllib/securitylabels/hr";
- private static final String SECURITY_LABELS_OD_PATH = "com/android/asllib/securitylabels/od";
-
- public static final List<String> OPTIONAL_FIELD_NAMES =
- List.of("isDataDeletable", "isDataEncrypted");
- public static final List<String> OPTIONAL_FIELD_NAMES_OD =
- List.of("is_data_deletable", "is_data_encrypted");
-
- private static final String ALL_FIELDS_VALID_FILE_NAME = "all-fields-valid.xml";
-
- /** Logic for setting up tests (empty if not yet needed). */
- public static void main(String[] params) throws Exception {}
-
- @Before
- public void setUp() throws Exception {
- System.out.println("set up.");
- }
-
- /** Test for all fields valid. */
- @Test
- public void testAllFieldsValid() throws Exception {
- System.out.println("starting testAllFieldsValid.");
- testHrToOdSecurityLabels(ALL_FIELDS_VALID_FILE_NAME);
- testOdToHrSecurityLabels(ALL_FIELDS_VALID_FILE_NAME);
- }
-
- /** Tests missing optional fields passes. */
- @Test
- public void testMissingOptionalFields() throws Exception {
- for (String optField : OPTIONAL_FIELD_NAMES) {
- var ele =
- TestUtils.getElementsFromResource(
- Paths.get(SECURITY_LABELS_HR_PATH, ALL_FIELDS_VALID_FILE_NAME));
- ele.get(0).removeAttribute(optField);
- SecurityLabels securityLabels = new SecurityLabelsFactory().createFromHrElements(ele);
- securityLabels.toOdDomElements(TestUtils.document());
- }
- for (String optField : OPTIONAL_FIELD_NAMES_OD) {
- var ele =
- TestUtils.getElementsFromResource(
- Paths.get(SECURITY_LABELS_OD_PATH, ALL_FIELDS_VALID_FILE_NAME));
- TestUtils.removeOdChildEleWithName(ele.get(0), optField);
- SecurityLabels securityLabels = new SecurityLabelsFactory().createFromOdElements(ele);
- securityLabels.toHrDomElements(TestUtils.document());
- }
- }
-
- private void testHrToOdSecurityLabels(String fileName) throws Exception {
- TestUtils.testHrToOd(
- TestUtils.document(),
- new SecurityLabelsFactory(),
- SECURITY_LABELS_HR_PATH,
- SECURITY_LABELS_OD_PATH,
- fileName);
- }
-
- private void testOdToHrSecurityLabels(String fileName) throws Exception {
- TestUtils.testOdToHr(
- TestUtils.document(),
- new SecurityLabelsFactory(),
- SECURITY_LABELS_OD_PATH,
- SECURITY_LABELS_HR_PATH,
- fileName);
- }
-}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java
deleted file mode 100644
index ec86d0f..0000000
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/ThirdPartyVerificationTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 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.asllib.marshallable;
-
-import com.android.asllib.testutils.TestUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ThirdPartyVerificationTest {
- private static final String THIRD_PARTY_VERIFICATION_HR_PATH =
- "com/android/asllib/thirdpartyverification/hr";
- private static final String THIRD_PARTY_VERIFICATION_OD_PATH =
- "com/android/asllib/thirdpartyverification/od";
-
- private static final String VALID_FILE_NAME = "valid.xml";
- private static final String MISSING_URL_FILE_NAME = "missing-url.xml";
-
- /** Logic for setting up tests (empty if not yet needed). */
- public static void main(String[] params) throws Exception {}
-
- @Before
- public void setUp() throws Exception {
- System.out.println("set up.");
- }
-
- /** Test for valid. */
- @Test
- public void testValid() throws Exception {
- System.out.println("starting testValid.");
- testHrToOdThirdPartyVerification(VALID_FILE_NAME);
- testOdToHrThirdPartyVerification(VALID_FILE_NAME);
- }
-
- /** Tests missing url. */
- @Test
- public void testMissingUrl() throws Exception {
- System.out.println("starting testMissingUrl.");
- hrToOdExpectException(MISSING_URL_FILE_NAME);
- odToHrExpectException(MISSING_URL_FILE_NAME);
- }
-
- private void hrToOdExpectException(String fileName) {
- TestUtils.hrToOdExpectException(
- new ThirdPartyVerificationFactory(), THIRD_PARTY_VERIFICATION_HR_PATH, fileName);
- }
-
- private void odToHrExpectException(String fileName) {
- TestUtils.odToHrExpectException(
- new ThirdPartyVerificationFactory(), THIRD_PARTY_VERIFICATION_OD_PATH, fileName);
- }
-
- private void testHrToOdThirdPartyVerification(String fileName) throws Exception {
- TestUtils.testHrToOd(
- TestUtils.document(),
- new ThirdPartyVerificationFactory(),
- THIRD_PARTY_VERIFICATION_HR_PATH,
- THIRD_PARTY_VERIFICATION_OD_PATH,
- fileName);
- }
-
- private void testOdToHrThirdPartyVerification(String fileName) throws Exception {
- TestUtils.testOdToHr(
- TestUtils.document(),
- new ThirdPartyVerificationFactory(),
- THIRD_PARTY_VERIFICATION_OD_PATH,
- THIRD_PARTY_VERIFICATION_HR_PATH,
- fileName);
- }
-}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
index f494240..8a0b35e 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/TransparencyInfoTest.java
@@ -29,9 +29,6 @@
"com/android/asllib/transparencyinfo/hr";
private static final String TRANSPARENCY_INFO_OD_PATH =
"com/android/asllib/transparencyinfo/od";
-
- private static final String VALID_EMPTY_FILE_NAME = "valid-empty.xml";
- private static final String WITH_DEVELOPER_INFO_FILE_NAME = "with-developer-info.xml";
private static final String WITH_APP_INFO_FILE_NAME = "with-app-info.xml";
@Before
@@ -39,22 +36,6 @@
System.out.println("set up.");
}
- /** Test for transparency info valid empty. */
- @Test
- public void testTransparencyInfoValidEmptyFile() throws Exception {
- System.out.println("starting testTransparencyInfoValidEmptyFile.");
- testHrToOdTransparencyInfo(VALID_EMPTY_FILE_NAME);
- testOdToHrTransparencyInfo(VALID_EMPTY_FILE_NAME);
- }
-
- /** Test for transparency info with developer info. */
- @Test
- public void testTransparencyInfoWithDeveloperInfo() throws Exception {
- System.out.println("starting testTransparencyInfoWithDeveloperInfo.");
- testHrToOdTransparencyInfo(WITH_DEVELOPER_INFO_FILE_NAME);
- testOdToHrTransparencyInfo(WITH_DEVELOPER_INFO_FILE_NAME);
- }
-
/** Test for transparency info with app info. */
@Test
public void testTransparencyInfoWithAppInfo() throws Exception {
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml
index 53794a1..03e71d2 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-safety-labels.xml
@@ -1,4 +1,4 @@
<app-metadata-bundles version="123456">
- <safety-labels version="12345">
+ <safety-labels>
</safety-labels>
</app-metadata-bundles>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml
index 00bcfa8..a00ef65 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-transparency-info.xml
@@ -1,4 +1,15 @@
<app-metadata-bundles version="123456">
-<transparency-info>
-</transparency-info>
+ <transparency-info>
+ <app-info
+ apsCompliant="false"
+ privacyPolicy="www.example.com">
+ <first-party-endpoints>
+ <item>url1</item>
+ </first-party-endpoints>
+ <service-provider-endpoints>
+ <item>url55</item>
+ <item>url56</item>
+ </service-provider-endpoints>
+ </app-info>
+ </transparency-info>
</app-metadata-bundles>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml
index 74644ed..f00fb26 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-safety-labels.xml
@@ -1,6 +1,5 @@
<bundle>
<long name="version" value="123456"/>
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
</pbundle_as_map>
</bundle>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml
index 63c5094..d0c8668 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-transparency-info.xml
@@ -1,4 +1,16 @@
<bundle>
<long name="version" value="123456"/>
- <pbundle_as_map name="transparency_info"/>
+ <pbundle_as_map name="transparency_info">
+ <pbundle_as_map name="app_info">
+ <boolean name="aps_compliant" value="false"/>
+ <string name="privacy_policy" value="www.example.com"/>
+ <string-array name="first_party_endpoints" num="1">
+ <item value="url1"/>
+ </string-array>
+ <string-array name="service_provider_endpoints" num="2">
+ <item value="url55"/>
+ <item value="url56"/>
+ </string-array>
+ </pbundle_as_map>
+ </pbundle_as_map>
</bundle>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml
index 883170a2..0d15efc 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/hr/all-fields-valid.xml
@@ -1,14 +1,11 @@
<app-info
- title="beervision"
- description="a beer app"
- containsAds="true"
- obeyAps="false"
- adsFingerprinting="false"
- securityFingerprinting="false"
- privacyPolicy="www.example.com"
- securityEndpoints="url1|url2|url3"
- firstPartyEndpoints="url1"
- serviceProviderEndpoints="url55|url56"
- category="Food and drink"
- email="max@maxloh.com"
- website="www.example.com" />
\ No newline at end of file
+ apsCompliant="false"
+ privacyPolicy="www.example.com">
+ <first-party-endpoints>
+ <item>url1</item>
+ </first-party-endpoints>
+ <service-provider-endpoints>
+ <item>url55</item>
+ <item>url56</item>
+ </service-provider-endpoints>
+</app-info>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml
index 6e976a3..bce5179 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/appinfo/od/all-fields-valid.xml
@@ -1,25 +1,12 @@
<pbundle_as_map name="app_info">
- <string name="title" value="beervision"/>
- <string name="description" value="a beer app"/>
- <boolean name="contains_ads" value="true"/>
- <boolean name="obey_aps" value="false"/>
- <boolean name="ads_fingerprinting" value="false"/>
- <boolean name="security_fingerprinting" value="false"/>
+ <boolean name="aps_compliant" value="false"/>
<string name="privacy_policy" value="www.example.com"/>
- <string-array name="security_endpoint" num="3">
- <item value="url1"/>
- <item value="url2"/>
- <item value="url3"/>
- </string-array>
- <string-array name="first_party_endpoint" num="1">
+ <string-array name="first_party_endpoints" num="1">
<item value="url1"/>
</string-array>
- <string-array name="service_provider_endpoint" num="2">
+ <string-array name="service_provider_endpoints" num="2">
<item value="url55"/>
<item value="url56"/>
</string-array>
- <string name="category" value="Food and drink"/>
- <string name="email" value="max@maxloh.com"/>
- <string name="website" value="www.example.com"/>
</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/missing-version.xml
deleted file mode 100644
index 762f3bd..0000000
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/missing-version.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<safety-labels>
-</safety-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml
index 7decfd4..92d10ca 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/valid-empty.xml
@@ -1 +1 @@
-<safety-labels version="12345"></safety-labels>
\ No newline at end of file
+<safety-labels></safety-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
index 84456da..ca3d9f0 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
@@ -1,4 +1,4 @@
-<safety-labels version="12345">
+<safety-labels>
<data-labels>
<data-shared dataType="location_data_type_approx_location"
isSharingOptional="false"
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml
index 940e48a..c962785 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-security-labels.xml
@@ -1,4 +1,4 @@
-<safety-labels version="12345">
+<safety-labels>
<security-labels
isDataDeletable="true"
isDataEncrypted="false"
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml
index bfbc5ae..0805290 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-third-party-verification.xml
@@ -1,4 +1,4 @@
-<safety-labels version="12345">
+<safety-labels>
<third-party-verification url="www.example.com">
</third-party-verification>
</safety-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml
deleted file mode 100644
index 3fbe359..0000000
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/missing-version.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<pbundle_as_map name="safety_labels">
-</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml
index 4f03d88..3fbe359 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/valid-empty.xml
@@ -1,3 +1,2 @@
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
index fa2a3f8..db92e02 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
@@ -1,5 +1,4 @@
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
<pbundle_as_map name="data_labels">
<pbundle_as_map name="data_shared">
<pbundle_as_map name="location">
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml
index b39c562b..9246180 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-security-labels.xml
@@ -1,5 +1,4 @@
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
<pbundle_as_map name="security_labels">
<boolean name="is_data_deletable" value="true" />
<boolean name="is_data_encrypted" value="false" />
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml
index 10653ff..fd435c5 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-third-party-verification.xml
@@ -1,5 +1,4 @@
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
<pbundle_as_map name="third_party_verification">
<string name="url" value="www.example.com"/>
</pbundle_as_map>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml
index a7c48fc..2512ca4 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/hr/with-app-info.xml
@@ -1,4 +1,14 @@
<transparency-info>
- <app-info title="beervision" description="a beer app" containsAds="true" obeyAps="false" adsFingerprinting="false" securityFingerprinting="false" privacyPolicy="www.example.com" securityEndpoints="url1|url2|url3" firstPartyEndpoints="url1" serviceProviderEndpoints="url55|url56" category="Food and drink" email="max@maxloh.com" />
+ <app-info
+ apsCompliant="false"
+ privacyPolicy="www.example.com">
+ <first-party-endpoints>
+ <item>url1</item>
+ </first-party-endpoints>
+ <service-provider-endpoints>
+ <item>url55</item>
+ <item>url56</item>
+ </service-provider-endpoints>
+ </app-info>
</transparency-info>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml
index b813641..c7bdd97 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/transparencyinfo/od/with-app-info.xml
@@ -1,26 +1,14 @@
<pbundle_as_map name="transparency_info">
<pbundle_as_map name="app_info">
- <string name="title" value="beervision"/>
- <string name="description" value="a beer app"/>
- <boolean name="contains_ads" value="true"/>
- <boolean name="obey_aps" value="false"/>
- <boolean name="ads_fingerprinting" value="false"/>
- <boolean name="security_fingerprinting" value="false"/>
+ <boolean name="aps_compliant" value="false"/>
<string name="privacy_policy" value="www.example.com"/>
- <string-array name="security_endpoint" num="3">
- <item value="url1"/>
- <item value="url2"/>
- <item value="url3"/>
- </string-array>
- <string-array name="first_party_endpoint" num="1">
+ <string-array name="first_party_endpoints" num="1">
<item value="url1"/>
</string-array>
- <string-array name="service_provider_endpoint" num="2">
+ <string-array name="service_provider_endpoints" num="2">
<item value="url55"/>
<item value="url56"/>
</string-array>
- <string name="category" value="Food and drink"/>
- <string name="email" value="max@maxloh.com"/>
</pbundle_as_map>
</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml
index b2ff449..cadf213 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/hr.xml
@@ -1,5 +1,5 @@
<app-metadata-bundles version="123">
- <safety-labels version="12345">
+ <safety-labels>
<data-labels>
<data-shared dataCategory="contacts"
dataType="contacts"
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml
index 81277bf..7aafd23 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/contacts/od.xml
@@ -1,7 +1,6 @@
<bundle>
<long name="version" value="123"/>
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
<pbundle_as_map name="data_labels">
<pbundle_as_map name="data_shared">
<pbundle_as_map name="contacts">
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
index 41b32b5..5923079 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
@@ -1,5 +1,5 @@
<app-metadata-bundles version="123">
- <safety-labels version="12345">
+ <safety-labels>
<data-labels>
<data-shared
dataType="location_data_type_approx_location"
@@ -10,24 +10,18 @@
isSharingOptional="true"
purposes="app_functionality|analytics" />
</data-labels>
- <security-labels
- isDataDeletable="true"
- isDataEncrypted="false"
- />
- <third-party-verification url="www.example.com">
- </third-party-verification>
</safety-labels>
<system-app-safety-label declaration="true">
</system-app-safety-label>
<transparency-info>
- <developer-info
- name="max"
- email="max@example.com"
- address="111 blah lane"
- countryRegion="US"
- relationship="aosp"
- website="example.com"
- registryId="registry_id" />
- <app-info title="beervision" description="a beer app" containsAds="true" obeyAps="false" adsFingerprinting="false" securityFingerprinting="false" privacyPolicy="www.example.com" securityEndpoints="url1|url2|url3" firstPartyEndpoints="url1" serviceProviderEndpoints="url55|url56" category="Food and drink" email="max@maxloh.com" />
+ <app-info apsCompliant="false" privacyPolicy="www.example.com">
+ <first-party-endpoints>
+ <item>url1</item>
+ </first-party-endpoints>
+ <service-provider-endpoints>
+ <item>url55</item>
+ <item>url56</item>
+ </service-provider-endpoints>
+ </app-info>
</transparency-info>
</app-metadata-bundles>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
index c11ac43..c24087e 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
@@ -1,7 +1,6 @@
<bundle>
<long name="version" value="123"/>
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
<pbundle_as_map name="data_labels">
<pbundle_as_map name="data_shared">
<pbundle_as_map name="location">
@@ -21,49 +20,21 @@
</pbundle_as_map>
</pbundle_as_map>
</pbundle_as_map>
- <pbundle_as_map name="security_labels">
- <boolean name="is_data_deletable" value="true"/>
- <boolean name="is_data_encrypted" value="false"/>
- </pbundle_as_map>
- <pbundle_as_map name="third_party_verification">
- <string name="url" value="www.example.com"/>
- </pbundle_as_map>
</pbundle_as_map>
<pbundle_as_map name="system_app_safety_label">
<boolean name="declaration" value="true"/>
</pbundle_as_map>
<pbundle_as_map name="transparency_info">
- <pbundle_as_map name="developer_info">
- <string name="name" value="max"/>
- <string name="email" value="max@example.com"/>
- <string name="address" value="111 blah lane"/>
- <string name="country_region" value="US"/>
- <long name="relationship" value="5"/>
- <string name="website" value="example.com"/>
- <string name="app_developer_registry_id" value="registry_id"/>
- </pbundle_as_map>
<pbundle_as_map name="app_info">
- <string name="title" value="beervision"/>
- <string name="description" value="a beer app"/>
- <boolean name="contains_ads" value="true"/>
- <boolean name="obey_aps" value="false"/>
- <boolean name="ads_fingerprinting" value="false"/>
- <boolean name="security_fingerprinting" value="false"/>
+ <boolean name="aps_compliant" value="false"/>
<string name="privacy_policy" value="www.example.com"/>
- <string-array name="security_endpoint" num="3">
- <item value="url1"/>
- <item value="url2"/>
- <item value="url3"/>
- </string-array>
- <string-array name="first_party_endpoint" num="1">
+ <string-array name="first_party_endpoints" num="1">
<item value="url1"/>
</string-array>
- <string-array name="service_provider_endpoint" num="2">
+ <string-array name="service_provider_endpoints" num="2">
<item value="url55"/>
<item value="url56"/>
</string-array>
- <string name="category" value="Food and drink"/>
- <string name="email" value="max@maxloh.com"/>
</pbundle_as_map>
</pbundle_as_map>
</bundle>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml
index ac844b3..a4242d0 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/hr.xml
@@ -1,5 +1,5 @@
<app-metadata-bundles version="123">
- <safety-labels version="12345">
+ <safety-labels>
<data-labels>
<data-shared dataCategory="location"
dataType="precise_location"
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml
index d0a3bfa..79afaf7 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/location/od.xml
@@ -1,7 +1,6 @@
<bundle>
<long name="version" value="123"/>
<pbundle_as_map name="safety_labels">
- <long name="version" value="12345"/>
<pbundle_as_map name="data_labels">
<pbundle_as_map name="data_shared">
<pbundle_as_map name="location">
diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS
index 33e237d..8e4569e 100644
--- a/tools/lint/OWNERS
+++ b/tools/lint/OWNERS
@@ -1,4 +1,5 @@
-brufino@google.com
+mattgilbride@google.com
+azharaa@google.com
jsharkey@google.com
per-file *CallingSettingsNonUserGetterMethods* = file:/packages/SettingsProvider/OWNERS
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 624a198..5c64697 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -41,6 +41,7 @@
PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
+ FeatureAutomotiveDetector.ISSUE,
)
override val api: Int
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt
new file mode 100644
index 0000000..972b0c9
--- /dev/null
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.google.android.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.ULiteralExpression
+import org.jetbrains.uast.UReferenceExpression
+
+/**
+ * A detector to check the usage of PackageManager.hasSystemFeature("
+ * android.hardware.type.automotive") in CTS tests.
+ */
+class FeatureAutomotiveDetector : Detector(), SourceCodeScanner {
+
+ companion object {
+
+ val EXPLANATION =
+ """
+ This class uses PackageManager.hasSystemFeature(\"android.hardware.type.automotive\") \
+ or other equivalent methods. \
+ If it is used to make a CTS test behave differently on AAOS, you should use \
+ @RequireAutomotive or @RequireNotAutomotive instead; otherwise, please ignore this \
+ warning. See https://g3doc.corp.google.com/wireless/android/partner/compatibility/\
+ g3doc/dev/write-a-test/index.md#write-a-test-that-behaves-differently-on-aaos
+ """
+
+ val ISSUE: Issue =
+ Issue.create(
+ id = "UsingFeatureAutomotiveInCTS",
+ briefDescription =
+ "PackageManager.hasSystemFeature(\"" +
+ " android.hardware.type.automotive\") is used in CTS tests",
+ explanation = EXPLANATION,
+ category = Category.TESTING,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(
+ FeatureAutomotiveDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+ }
+
+ override fun getApplicableMethodNames() =
+ listOf("hasSystemFeature", "hasFeature", "hasDeviceFeature", "bypassTestForFeatures")
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ node.valueArguments.forEach {
+ val value =
+ when (it) {
+ is ULiteralExpression -> it.value
+ is UReferenceExpression -> ConstantEvaluator.evaluate(context, it)
+ else -> null
+ }
+ if (value is String && value == "android.hardware.type.automotive") {
+ context.report(
+ issue = ISSUE,
+ location = context.getNameLocation(method),
+ message = EXPLANATION
+ )
+ }
+ }
+ }
+}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt
new file mode 100644
index 0000000..b5e2c00
--- /dev/null
+++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt
@@ -0,0 +1,366 @@
+/*
+ * 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.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+@Suppress("UnstableApiUsage")
+class FeatureAutomotiveDetectorTest : LintDetectorTest() {
+ val explanation =
+ FeatureAutomotiveDetector.EXPLANATION.replace("\\", "").replace("\n ", "") +
+ " [UsingFeatureAutomotiveInCTS]"
+
+ override fun getDetector(): Detector = FeatureAutomotiveDetector()
+ override fun getIssues(): List<Issue> = listOf(FeatureAutomotiveDetector.ISSUE)
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ @Test
+ fun testWarning1() {
+ lint()
+ .files(
+ java(
+ """
+ import android.content.pm.PackageManager;
+
+ public class Foo {
+
+ private void fun() {
+ PackageManager.getInstance().hasSystemFeature(
+ "android.hardware.type.automotive");
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/android/content/pm/PackageManager.java:13: Warning: $explanation
+ public boolean hasSystemFeature(String feature) {
+ ~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testWarning2() {
+ lint()
+ .files(
+ java(
+ """
+ import android.content.pm.PackageManager;
+
+ public class Foo {
+
+ private void fun() {
+ String featureName = "android.hardware.type.automotive";
+ PackageManager.getInstance().hasSystemFeature(featureName);
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/android/content/pm/PackageManager.java:13: Warning: $explanation
+ public boolean hasSystemFeature(String feature) {
+ ~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testWarning3() {
+ lint()
+ .files(
+ java(
+ """
+ import android.content.pm.PackageManager;
+
+ public class Foo {
+
+ private void fun() {
+ PackageManager.getInstance().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/android/content/pm/PackageManager.java:13: Warning: $explanation
+ public boolean hasSystemFeature(String feature) {
+ ~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testWarning4() {
+ lint()
+ .files(
+ java(
+ """
+ import android.content.pm.PackageManager;
+
+ public class Foo {
+
+ private void fun() {
+ String featureName = PackageManager.FEATURE_AUTOMOTIVE;
+ PackageManager.getInstance().hasSystemFeature(featureName);
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/android/content/pm/PackageManager.java:13: Warning: $explanation
+ public boolean hasSystemFeature(String feature) {
+ ~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testWarning5() {
+ lint()
+ .files(
+ java(
+ """
+ import com.android.example.Utils;
+
+ public class Foo {
+
+ private void fun() {
+ Utils.hasFeature("android.hardware.type.automotive");
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/com/android/example/Utils.java:7: Warning: $explanation
+ public static boolean hasFeature(String feature) {
+ ~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testWarning6() {
+ lint()
+ .files(
+ java(
+ """
+ import com.android.example.Utils;
+
+ public class Foo {
+
+ private void fun() {
+ Utils.hasDeviceFeature("android.hardware.type.automotive");
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/com/android/example/Utils.java:11: Warning: $explanation
+ public static boolean hasDeviceFeature(String feature) {
+ ~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testWarning7() {
+ lint()
+ .files(
+ java(
+ """
+ import com.android.example.Utils;
+
+ public class Foo {
+
+ private void fun() {
+ Utils.hasFeature(new Object(), "android.hardware.type.automotive");
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/com/android/example/Utils.java:15: Warning: $explanation
+ public static boolean hasFeature(Object object, String feature) {
+ ~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testWarning8() {
+ lint()
+ .files(
+ java(
+ """
+ import com.android.example.Utils;
+
+ public class Foo {
+
+ private void fun() {
+ Utils.bypassTestForFeatures("android.hardware.type.automotive");
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/com/android/example/Utils.java:19: Warning: $explanation
+ public static boolean bypassTestForFeatures(String feature) {
+ ~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ @Test
+ fun testNoWarning() {
+ lint()
+ .files(
+ java(
+ """
+ import android.content.pm.PackageManager;
+
+ public class Foo {
+ private void fun() {
+ String featureName1 = "android.hardware.type.automotive";
+ String featureName2 = PackageManager.FEATURE_AUTOMOTIVE;
+ String notFeatureName = "FEATURE_AUTOMOTIVE";
+ PackageManager.getInstance().hasSystemFeature(notFeatureName);
+ /*
+ PackageManager.getInstance().hasSystemFeature(
+ "android.hardware.type.automotive");
+ */
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(FeatureAutomotiveDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+
+ private val pmStub: TestFile =
+ java(
+ """
+ package android.content.pm;
+
+ import java.lang.String;
+
+ public class PackageManager {
+ public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+ public static PackageManager getInstance() {
+ return new PackageManager();
+ }
+
+ public boolean hasSystemFeature(String feature) {
+ return true;
+ }
+ }
+ """
+ )
+
+ private val exampleStub: TestFile =
+ java(
+ """
+ package com.android.example;
+
+ import java.lang.String;
+
+ public class Utils {
+ public static boolean hasFeature(String feature) {
+ return true;
+ }
+
+ public static boolean hasDeviceFeature(String feature) {
+ return true;
+ }
+
+ public static boolean hasFeature(Object object, String feature) {
+ return true;
+ }
+
+ public static boolean bypassTestForFeatures(String feature) {
+ return true;
+ }
+ }
+ """
+ )
+
+ private val stubs = arrayOf(pmStub, exampleStub)
+}